传递.net数据表到MATLAB

本文关键字:MATLAB 数据表 net 传递 | 更新日期: 2023-09-27 18:09:31

我正在为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是我自己的例程,它满足null, DBNull和返回double。NaN,因为Matlab将所有null视为NaN。

是这样的;有人知道MATLAB数据类型,允许我在多个数据类型的连续数组传递吗?我能想到的唯一解决方法是使用MWStructArraysMWStructArray,但这似乎很黑客,我不确定它在MATLAB代码中的工作效果如何,所以我想尝试找到一个更优雅的解决方案,如果可以的话。我已经看过使用MWCellArray,但它给了我一个编译错误,当我试图实例化它。

我希望能做这样的事情;

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

但是正如我所说的,我得到了一个编译错误,也与传递一个对象数组给构造函数。

如果我错过了什么愚蠢的道歉。我已经做了一些谷歌搜索,但关于Matlab到。net接口的信息似乎有点轻,所以这就是我在这里发布它的原因。

提前感谢。

[编辑]

感谢大家的建议。

对于我们的特定实现来说,最快和最有效的方法是在SQL代码中将Datetime转换为int。

然而,在其他方法中,我建议使用MWCharArray方法。它使用最少的麻烦,结果证明我只是做错了-你不能像对待另一个MWArray类型一样对待它,因为它当然是为处理多种数据类型而设计的,你需要迭代它,坚持使用MWNumerics或任何你喜欢的东西。需要注意的一点是,mwarray是基于1的,而不是基于0的。

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

传递.net数据表到MATLAB

正如@Matt在评论中建议的那样,如果你想存储不同的数据类型(数字,字符串,结构体等),你应该使用这个托管API暴露的等效单元数组,即MWCellArray类。

为了说明,我实现了一个简单的。net程序集。它公开了一个MATLAB函数,该函数接收一个单元数组(来自数据库表的记录),并简单地打印它们。这个函数将从我们的c#应用程序中调用,该应用程序生成一个示例DataTable,并将其转换为MWCellArray(逐单元格填充表项)。 技巧是将DataTable中包含的对象映射为MWArray派生类所支持的类型。以下是我使用的(查看文档以获取完整列表):
.NET native type          MWArray classes
------------------------------------------
double,float,int,..       MWNumericArray
string                    MWCharArray
DateTime                  MWNumericArray       (using Ticks property)
关于日期/时间数据的说明:在。net中,系统。DateTime将日期和时间表示为:

表示自1月份以来经过的100纳秒间隔的数量1,0001 at 00:00:00.000

而在MATLAB中,这是DATENUM函数必须说的:

日期序号表示日期的整数和小数从特定日期和时间开始的天数,其中datenum('Jan-1-000000:00:00')返回数字1

因此,我在c#应用程序中编写了两个辅助函数来转换DateTime"刻度"以匹配串行日期数的MATLAB定义。


首先,考虑这个简单的MATLAB函数。它期望接收一个包含表数据的numRos-by-numCols cellarray。在我的例子中,列是:Name (string), Price (double), Date (DateTime)
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代码,并让matlab直接查询数据库,使用您的。net接口,而不是试图通过您描述的这个序列化过程。我在我们的环境中多次这样做,并取得了巨大的成功。在这样的努力中Net.addAssembly是你最好的朋友。

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

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

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

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

我尝试的是:

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

在某些年份,例如2014年、2015年,1月1日00:00:00的日期似乎有问题。例如,

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日,它是OK的。有什么建议或者为什么会这样吗?

我有和@Johan一样的问题。问题是闰年不能正确计算日期

为了解决这个问题,我更改了将DateTime转换为以下代码:

private static long MatlabDateConversionFactor = (10000000L * 3600L * 24L);
private static long tickDiference = 367;
public static double convertToMATLABDateNum(DateTime dt) {
    var converted = ((double)dt.Ticks / (double)MatlabDateConversionFactor);
    return converted + tickDiference;
}
public static DateTime convertFromMATLABDateNum(double datenum) {
    var ticks = (long)((datenum - 367) * MatlabDateConversionFactor);
    return new DateTime(ticks, DateTimeKind.Utc);
}