Blog

Using Excel with NMath

We’ve had several customers ask about porting their Excel model to a .NET language in order to leverage the performance and functionality of NMath or NMath Stats. NMath does have good crossover functionality with Excel making this porting job easier. It is also possible to accelerate your Excel models by calling the NMath .NET assemblies directly from a VBA macro in Excel . This post provides some guidance for porting all or just a portion of your Excel model to C# and NMath.

Excel is designed to interoperate with external assemblies from VBA using COM. Type libraries built in .NET are not directly COM compatible, however all .NET class libraries including NMath and NMath Stats can be made to present a COM interface making Excel interop possible. This interop is acheived by building the COM type library directly from the assembly – no recompiling needed – using a tool shipped with the .NET framework, and then adding this type library as a reference to an Excel sheets’ VBA macro. Because of the many differences between the C# language and VBA, only a small portion of NMath will be accessable from Excel using this procedure, however a simple remedy will be outlined below that can expand the available functionality to all of NMath.

  1. To build the type library interface to the NMath.dll use the regasm.exe utility shipped with the .NET framework
     >regasm.exe NMath.dll /tlb:NMathCom.tlb

    The COM compatible type library now resides in the NMathCom.tlb file. You will see some warning messages regarding incompatibilities between COM and NMath.

  2. Open a spreadsheet, right click on a sheet tab and choose View Code to open the VBA development environment.
  3. In the Tools menu select References… and browse to the location of the new NMath type library.

Now the COM compatible portions of NMath are now available for use from VBA. At this point we can code up simple example for generating random numbers in VBA to test out the NMath interoperability

Private Sub TestNMath()
  Dim rand As New NMath.RandGenLogNormal
  rand.Mean = 50
  rand.Variance = 10
 
  Dim i As Integer
  For i = 1 To 10
    Cells(i, 1) = rand.Next()
  Next
End Sub

This simple VBA script populates cells A1:A10 with LogNormal distributed random numbers with a mean of 50 and a variance of 10. This is not so easily achieved within Excel natively.

Because NMath was not designed from the ground up to interoperate with the aging COM standard, many types will not be useable from VBA. All types lacking an empty (default) constructor, generic classes, static methods, and all static classes are not directly usable from VBA. However, any of these incompatible classes or methods can be wrapped in VB and then made available to Excel via COM. We understand many Excel users are not C#/.NET experts and so we are happy to help wrapping any NMath classes or NMath Stats classes you may need for enhancing and accelerating your Excel models.

Porting Excel Models to C# using NMath

As models grow in Excel, they commonly devolve into a bizantine workbook that becomes slow, opaque, and difficult to version, debug and manage. Usually, it is at this point that users start looking at porting the complex portions to another compatible platform, typically VB. These workbooks tend to be business critical applications so the port must be done carefully and in a piecewise fashion. This conservative strategy can be acheived using the following steps.

  1. Identify the computationally demanding and complex portion of the spreadsheet carefully selecting a separation point where the interface is thin between the workbook and the new VB class.
  2. Identify all of the input and output cells of this computation.
  3. Create build a VB wrapper class in a (class library) VB project that wraps the functionality necessary for the port. This class should include methods for loading and returning results in types compatible with VBA.
  4. Build the library and generate the COM type library and add this new type library to the workbook’s VBA.
  5. Test the new model in parallel with the existing model.
  6. Remove the duplicated Excel computation and enjoy the new faster model.

Porting functionality from an Excel model to a VB type library can be facilitated by NMath due to the large crossover in functionality between Excel and NMath Stats. Also, due to NMath’s high performance, once ports are complete, considerable performance gains can be expected. Below is a table of Excel functions and their supporting classes in either NMath Stats or native .NET.

Happy Computing,

-Paul

Resources

  • Microsoft ported the Excel financial functions to F#, making them accessible to any VB or C# project. Follow this link to download this library and read the libraries’ documentation and limitations.
  • Microsoft’s landing page on the assembly registration tool regasm.exe for building the COM type libraries.
  • A little dated but clear and complete article on accessing .NET assemblies from Excel.

Excel Functions Supported in NMath and .NET

Between the NMath and NMath Stats numeric libraries and the .NET framework many of the Excel functions are covered for a port to C#. If you have a math function that you need which is not covered or is not in this list, let us know and we can probably add it to NMath or NMath Stats. Note resources above if financial functions are needed.

Excel Function Framework/NameSpace Class/Method
ABS NMath.Core NMathFunctions.Abs()

ACOS NMath.Core NMathFunctions.ACos()

AND NMath.Core NMathFunctions.And()

ASIN NMath.Core NMathFunctions.ASin()

AREAS No
ASIN NMath.Core NMathFunctions.Asin()

ASINH No
ATAN NMath.Core NMathFunctions.Atan()

ATAN2 NMath.Core NMathFunctions.Atan2()

ATANH No
AVEDEV NMath.Stats StatsFunctions.MeanDeviation()

AVERAGE NMath.Stats StatsFunctions.Mean()

AVERAGEA No
BETADIST NMath.Stats BetaDistribution class

BETAINV NMath.Stats InverseCDF method in BetaDistribution class

BINOMDIST NMath.Stats BinomialDistribution class

CALL .NET
CEILING NMath.Core NMathFunctions.Ceil()

CELL No
CHAR No
CHIDIST NMath.Stats ChiSquareDistribution class

CHIINV NMath.Stats InverseCDF method in ChiSquareDistribution class

CHITEST No
CHOOSE No
CLEAN No
CODE No
COLUMN No
COLUMNS NMath.Core DataFrame class using Cols property

COMBIN NMath.Stats StatsFunctions.Binomial()

CONCATENATE .NET
CONFIDENCE NMath.Stats OneSampleZTest class using LowerConfidenceBound or UpperConfidenceBound properties

CORREL NMath.Stats StatsFunctions.Correlation()

COS NMath.Core NMathFunctions.Cos()

COSH NMath.Core NMathFunctions.Cosh()

COUNT NMath.Stats StatsFunctions.Count()

COUNTBLANK NMath.Stats If blanks are represented by NaNs then use StatsFunctions.NaNCount()

COUNTIF NMath.Stats StatsFunctions.CountIf()

COVAR NMath.Stats StatsFunctions.Covariance()

CRITBINOM NMath.Stats BinomialDistribution class

DATE .NET
DATEVALUE .NET
DAVERAGE NMath.Stats StatsFunctions.If() to get Subset then StatsFunctions.Mean()

DAY .NET
DAYS360 .NET
DB No
DCOUNT NMath.Stats StatsFunctions.CountIf()

DCOUNTA NMath.Stats If blanks are represented by NaNs then use StatsFunctions.NaNCountIf()

DDB No
DEGREES No
DEVSQ NMath.Stats StatsFunctions.SumOfSquares()

DGET NMath.Stats StatsFunctions.If() and then index property on resulting DataFrame

DMAX NMath.Stats StatsFunctions.If() to create Subset then StatsFunctions.Max()

DMIN NMath.Stats StatsFunctions.If() to create Subset then StatsFunctions.Min()

DOLLAR .NET
DPRODUCT NMath.Stats StatsFunctions.If() to create Subset then to DoubleVector() then NMathFunctions.Product()

DSTDEV NMath.Stats StatsFunctions.If() to create Subset then StatsFunctions.StandardDeviation(BiasType.Unbiased)

DSTDEVP NMath.Stats StatsFunctions.If() to create Subset then StatsFunctions.StandardDeviation()

DSUM NMath.Stats StatsFunctions.SumIf()

DVAR NMath.Stats StatsFunctions.If() to create Subset then toDoubleVector() then NMathFunctions.Variance(BiasType.Unbiased)

DVARP NMath.Stats StatsFunctions.If() to create Subset then toDoubleVector() then NMathFunctions.Variance()

ERROR.TYPE No
EVEN .NET
EXACT .NET
EXP NMath.Core NMathFunctions.Exp()

EXPONDIST NMath.Stats ExponentialDistribution class

FACT NMath.Stats StatsFunctions.Factorial()

FALSE .NET
FDIST NMath.Stats FDistribution class

FIND .NET
FINV NMath.Stats InverseCDF method in FDistribution class

FISHER No
FISHERINV No
FIXED .NET
FLOOR NMath.Core NMathFunctions.Floor()

FORECAST NMath.Stats LinearRegression class in Stats

FREQUENCY NMath.Stats StatsFunctions.Mode() for frequency, use StatsFunctions.If() to find the indicies.

FTEST NMath.Stats TwoSampleFTest class in Stats

FV No
GAMMADIST NMath.Stats GammaDistribution class

GAMMAINV NMath.Stats InverseCDF method in GammaDistribution class

GAMMALN NMath.Stats StatsFunctions.GammaLn()

GEOMEAN NMath.Stats StatsFunctions.GeometricMean()

GETPIVOTDATA No
GROWTH NMath.Stats LinearRegression class in Stats

HARMEAN NMath.Stats StatsFunctions.HarmonicMean()

HLOOKUP No
HOUR .NET
HYPERLINK .NET
HYPGEOMDIST No
IF NMath.Stats StatsFunctions.If()

INDEX NMath.Core Index properties in vector, matrices, columns and frames

INDIRECT No
INFO .NET
INT .NET
INTERCEPT NMath.Stats LinearRegression class in Stats

IPMT No
IRR No
ISBLANK NMath.Core Can use NaN to indicate missing value. Then use Double.IsNaN(cell) to verify.

ISERROR No
ISLOGICAL No
ISNA No
ISNONTEXT No
ISNUMBER No
ISPMT No
ISREF No
ISTEXT No
KURT NMath.Stats StatsFunctions.Kurtosis()

LARGE NMath.Stats StatsFunctions.Sort() with StatsFunctions.Percentile()

LEFT .NET
LEN .NET
LINEST NMath.Stats LeastSquares class

LN NMath.Core NMathFunctions.Log()

LOG No
LOG10 NMath.Core NMathFunctions.Log10()

LOGEST NMath.Stats LinearRegression class in Stats

LOGINV NMath.Stats InverseCDF method in LognormalDistribution class

LOGNORMDIST NMath.Stats LognormalDistribution class

LOOKUP NMath.Stats IndexOf(), IndicesOf() methods in DataFrame class

LOWER .NET
MATCH No
MAX NMath.Stats StatsFunctions.Max()

MAXA No
MDETERM NMath.Stats Use LUFact class to factorize then call Determinant() method to compute.

MEDIAN NMath.Stats StatsFunctions.Median()

MID .NET
MIN NMath.Stats StatsFunctions.Min()

MINA No
MINUTE .NET
MINVERSE NMath.Stats Use LUFact class to factorize then call Inverse() method to compute.

MIRR No
MMULT NMath.Core Call Multiply() on a matrix class

MOD .NET
MODE NMath.Stats StatsFunctions.Mode()

MONTH .NET
N No
NA .NET
NEGBINOMDIST NMath.Stats NegativeBinomialDistribution class

NORMDIST NMath.Stats NormalDistribution class

NORMINV NMath.Stats InverseCDF() in NormalDistribution class

NORMSDIST NMath.Stats NormalDistribution class

NORMSINV NMath.Stats InverseCDF() in NormalDistribution class

NOT .NET
NOW .NET
NPER No
NPV No
ODD .NET
OFFSET No
OR .NET
PEARSON NMath.Stats StatsFunctions.Correlation()

PERCENTILE NMath.Stats StatsFunctions.Percentile()

PERCENTRANK NMath.Stats StatsFunctions.PercentileRank()

PERMUT NMath.Stats Factorial() in StatsFunctions

PI .NET System.Math.PI
PMT No
POISSON NMath.Stats PoissonDistribution class

POWER NMath.Stats NMathFunctions.Pow

PPMT No
PROB NMath.Stats PDF methods in all of the distribution classes

PRODUCT NMath.Stats NMathFunctions.Product()

PROPER .NET
PV No
QUARTILE NMath.Stats StatsFunctions.Quartile()

RADIANS .NET
RAND NMath.Stats Many RandomNumberGenerator classes in NMath Core

RANK NMath.Stats StatsFunctions.Rank()

RATE No
REGISTER.ID No
REPLACE .NET
REPT .NET
RIGHT .NET
ROMAN No
ROUND NMath.Core NMathFunctions.Round()

ROUNDDOWN NMath.Core NMathFunctions.Round()

ROUNDUP NMath.Core NMathFunctions.Round()

ROW NMath.Stats Row property on DataFrame

ROWS NMath.Stats Rows property on DataFrame

RSQ No
SEARCH .NET
SECOND .NET
SIGN .NET
SIN NMath.Core NMathFunctions.Sin()

SINH NMath.Core NMathFunctions.Sinh()

SKEW NMath.Stats StatsFunctions.Skewness() for samples or Skewness properties on Distribution classes.

SLN No
SLOPE NMath.Stats LinearRegression class in Stats

SMALL NMath.Stats StatsFunctions.Sort() and then index or find percentage of count and use StatsFunctions.Percentile()

SQRT NMath.Core NMathFunctions.Sqrt()

STANDARDIZE NMath.Stats NormalDistribuion class

STDEV NMath.Stats StatsFunctions.StandardDeviation(BiasType.Unbiased)

STDEVA No
STDEVP NMath.Stats StatsFunctions.StandardDeviation()

STDEVPA No
STEYX NMath.Stats LinearRegression class in Stats

SUBSTITUTE .NET
SUBTOTAL NMath.Stats Use Slice/Subset/Range to get portion of data then StatsFunctions.Sum(), Or, StatsFunctions.SumIf()

SUM NMath.Stats StatsFunctions.Sum()

SUMIF NMath.Stats StatsFunctions.SumIf()

SUMPRODUCT NMath.Stats Use operator* on vectors then NMathFunctions.Sum()

SUMSQ NMath.Stats StatsFunctions.SumOfSquares()

SUMX2MY2 NMath.Core Square each vector using NMathFunctions.Pow(2) then us operator- for difference then NMathFunctions.Sum()

SUMX2PY2 NMath.Core Square each vector using NMathFunctions.Pow(2) then use operator+ for difference then NMathFunctions.Sum()

SUMXMY2 NMath.Stats use operator- to find difference then Product(2) then NMathFunctions.Sum()

SYD No
T No
TAN NMath.Core NMathFunctions.Tan()

TANH NMath.Core NMathFunctions.Tanh()

TDIST NMath.Stats TDistribution class

TEXT No
TIME .NET
TIMEVALUE .NET
TINV NMath.Stats InverseCDF() in TDistribution class

TODAY .NET
TRANSPOSE NMath.Core Transpose() method on a matrix

TREND NMath.Stats LinearRegression class in Stats

TRIM NMath.Stats StatsFunctions.Trim()

TRIMMEAN NMath.Stats StatsFunctions.TrimmedMean()

TRUE .NET
TRUNC .NET
TTEST NMath.Stats TwoSamplePairedTTest or TwoSampleUnpairedTTest classes

TYPE .NET
UPPER .NET
VALUE .NET
VAR NMath.Stats StatsFunctions.Variance(BiasType.Unbiased)

VARA No
VARP NMath.Stats StatsFunctions.Variance()

VARPA No
VDB No
VLOOKUP NMath.Stats Combination of IndexOf() method on DataFrame and index operators

WEEKDAY .NET
WEIBULL NMath.Stats WeibullDistribution

ZTEST NMath.Stats OneSampleZTest

  • Share/Bookmark

Tags: , , , , ,

Leave a Reply