将.NET数据表传递给MATLAB

我正在为Matlab组件构建一个接口层,该组件用于分析由我也在构建的单独.NET应用程序维护的数据。 我正在尝试将.NET数据表序列化为数值数组,以传递给MATLAB组件(作为更通用的序列化例程的一部分)。

到目前为止,我已经相当成功地传递了数值数据表,但是在尝试添加数据类型DateTime列时遇到了麻烦。 我到目前为止所做的是将DataTable的值填充到双数组中,因为MATLAB只关心双精度,然后直接转换为MWNumericArray ,它本质上是一个矩阵。

这是当前的代码;

 else if (sourceType == typeof(DataTable)) { DataTable dtSource = source as DataTable; var rowIdentifiers = new string[dtSource.Rows.Count]; // I know this looks silly but we need the index of each item // in the string array as the actual value in the array as well for (int i = 0; i < dtSource.Rows.Count; i++) { rowIdentifiers[i] = i.ToString(); } // convenience vars int rowCount = dtSource.Rows.Count; int colCount = dtSource.Columns.Count; double[,] values = new double[rowCount, colCount]; // For each row for (int rownum = 0; rownum < rowCount; rownum++) { // for each column for (int colnum = 0; colnum < colCount; colnum++) { // ASSUMPTION. value is a double values[rownum, colnum] = Conversion.ConvertToDouble(dtSource.Rows[rownum][colnum]); } } return (MWNumericArray)values; } 

Conversion.ConvertToDouble是我自己的例程,它满足NULLS,DBNull并返回double.NaN,因为Matlab将所有NULLS视为NaN。

所以这就是事情; 有谁知道MATLAB数据类型允许我传入一个具有多种数据类型的连续数组? 我能想到的唯一解决方法是使用MWStructArrayMWStructArrays ,但这看起来很MWStructArrays ,我不确定它在MATLAB代码中的效果如何,所以如果可以,我想尝试找到更优雅的解决方案。 我已经看过使用MWCellArray ,但是当我尝试实例化它时,它给了我一个编译错误。

我希望能够做类似的事情;

 object[,] values = new object[rowCount, colCount]; // fill loosely-typed object array return (MWCellArray)values; 

但正如我所说,我得到了一个编译错误,同时将一个对象数组传递给构造函数。

如果我错过任何愚蠢的事情,我会道歉。 我做了一些谷歌搜索,但有关Matlab到.NET界面的信息似乎有点轻,所以这就是我在这里发布的原因。

提前致谢。

[编辑]

感谢大家的建议。

事实certificate,我们特定实现的最快和最有效的方法是将Datetime转换为SQL代码中的int。

但是,在其他方法中,我建议使用MWCharArray方法。 它使用最少的麻烦,事实certificate我只是做错了 – 你不能把它当作另一种MWArray类型,因为它当然是为了处理你需要迭代它的多种数据类型,坚持MWNumerics或随你随心所欲的任何东西。 需要注意的一点是,MWArrays是基于1的,而不是基于0的。 那个人一直在抓我。

我今天晚些时候会在有空的时候进行更详细的讨论,但现在我没有。 再次感谢大家的帮助。

正如@Matt在评论中建议的那样,如果要存储不同的数据类型(数字,字符串,结构等等),则应使用此托管API公开的等效单元arrays,即MWCellArray类。

为了说明,我实现了一个简单的.NET程序集。 它公开了一个MATLAB函数,它接收一个单元格数组(来自数据库表的记录),并简单地打印它们。 这个函数将从我们的C#应用​​程序中调用,该应用程序生成一个示例DataTable ,并将其转换为MWCellArrayMWCellArray细胞填充表条目)。

诀窍是通过MWArray派生的类将DataTable包含的对象映射到支持的类型。 以下是我使用的(查看完整列表的文档 ):

 .NET native type MWArray classes ------------------------------------------ double,float,int,.. MWNumericArray string MWCharArray DateTime MWNumericArray (using Ticks property) 

关于日期/时间数据的注释:在.NET中, System.DateTime将日期和时间表示为:

自0001年1月1日00:00:00000以来经过的100纳秒间隔的数量

而在MATLAB中,这就是DATENUM函数所说的:

序列日期编号表示特定日期和时间的整数和小数天数,其中datenum(’Jan-1-0000 00:00:00’)返回数字1

出于这个原因,我在C#应用程序中编写了两个辅助函数来转换DateTime “ticks”以匹配MATLAB对序列日期编号的定义。


首先,考虑这个简单的MATLAB函数。 它希望收到包含表数据的numRos-by-numCols cellarray。 在我的示例中,列是:名称(字符串),价格(双),日期(日期时间)

 function [] = my_cell_function(C) names = C(:,1); price = cell2mat(C(:,2)); dt = datevec( cell2mat(C(:,3)) ); disp(names) disp(price) disp(dt) end 

使用MATLAB Builder NE中的deploytool ,我们将上面的内容构建为.NET程序集。 接下来,我们创建一个C#控制台应用程序,然后添加对MWArray.dll程序集的引用,以及上面生成的引用。 这是我正在使用的程序:

 using System; using System.Data; using MathWorks.MATLAB.NET.Utility; // MWArray.dll using MathWorks.MATLAB.NET.Arrays; // MWArray.dll using CellExample; // CellExample.dll assembly created namespace CellExampleTest { class Program { static void Main(string[] args) { // get data table DataTable table = getData(); // create the MWCellArray int numRows = table.Rows.Count; int numCols = table.Columns.Count; MWCellArray cell = new MWCellArray(numRows, numCols); // one-based indices // fill it cell-by-cell for (int r = 0; r < numRows; r++) { for (int c = 0; c < numCols; c++) { // fill based on type Type t = table.Columns[c].DataType; if (t == typeof(DateTime)) { //cell[r+1,c+1] = new MWNumericArray( convertToMATLABDateNum((DateTime)table.Rows[r][c]) ); cell[r + 1, c + 1] = convertToMATLABDateNum((DateTime)table.Rows[r][c]); } else if (t == typeof(string)) { //cell[r+1,c+1] = new MWCharArray( (string)table.Rows[r][c] ); cell[r + 1, c + 1] = (string)table.Rows[r][c]; } else { //cell[r+1,c+1] = new MWNumericArray( (double)table.Rows[r][c] ); cell[r + 1, c + 1] = (double)table.Rows[r][c]; } } } // call MATLAB function CellClass obj = new CellClass(); obj.my_cell_function(cell); // Wait for user to exit application Console.ReadKey(); } // DateTime <-> datenum helper functions static double convertToMATLABDateNum(DateTime dt) { return (double)dt.AddYears(1).AddDays(1).Ticks / (10000000L * 3600L * 24L); } static DateTime convertFromMATLABDateNum(double datenum) { DateTime dt = new DateTime((long)(datenum * (10000000L * 3600L * 24L))); return dt.AddYears(-1).AddDays(-1); } // return DataTable data static DataTable getData() { DataTable table = new DataTable(); table.Columns.Add("Name", typeof(string)); table.Columns.Add("Price", typeof(double)); table.Columns.Add("Date", typeof(DateTime)); table.Rows.Add("Amro", 25, DateTime.Now); table.Rows.Add("Bob", 10, DateTime.Now.AddDays(1)); table.Rows.Add("Alice", 50, DateTime.Now.AddDays(2)); return table; } } } 

由编译的MATLAB函数返回的此C#程序的输出:

 'Amro' 'Bob' 'Alice' 25 10 50 2011 9 26 20 13 8.3906 2011 9 27 20 13 8.3906 2011 9 28 20 13 8.3906 

一种选择是直接从matlab打开.NET代码,并使用.net接口直接查询数据库,而不是尝试通过您描述的序列化过程。 我在我们的环境中反复这样做,取得了巨大的成功。 在这样的努力中, Net.addAssembly是你最大的朋友。

细节在这里。 http://www.mathworks.com/help/matlab/ref/net.addassembly.html

第二个选择是使用Matlab Cell Array。 您可以对其进行设置,因此列是不同的数据类型,每列构成一个单元格。 这是matlab本身在textscan函数中使用的技巧。 我建议您在此处阅读该function的文档: http : //www.mathworks.com/help/techdoc/ref/textscan.html

第三种选择是完全使用文本扫描。 从.net代码中写出一个文本文件,让textscan处理它的解析。 Textscan是将这种数据输入matlab的非常强大的机制。 您可以将文本扫描指向文件或一串字符串。

我已经尝试过@Amro编写的函数,但某些日期的结果不正确。

我尝试的是:

  1. 用C#创建一个日期
  2. 使用函数转换为@Amro提供的Matlab日期num
  3. 在Matlab中使用该数字来检查其正确性

它似乎与1月1日00:00:00的日期有关,例如2014年,2015年。例如,

 DateTime dt = new DateTime(2014, 1, 1, 0, 0, 0); double dtmat = convertToMATLABDateNum(dt); 

我从这里得到了dtmat = 735599.0。 我在Matlab中使用如下:

 datestr(datenum(735599.0)) 

我得到了这个回报:

 ans = 31-Dec-2013 

当我尝试2012年1月1日时,没关系。 任何建议或为什么会发生这种情况?