将 .NET 数据表传递给 MATLAB

发布于 2024-12-06 03:32:16 字数 2270 浏览 0 评论 0原文

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

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

这是当前的代码;

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 数据类型可以让我传入具有多种数据类型的连续数组吗?我能想到的唯一解决方法是使用 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 开始的。那个人一直在追赶我。

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

I'm building an interface layer for a Matlab component which is used to analyse data maintained by a separate .NET application which I am also building. I'm trying to serialise a .NET datatable as a numeric array to be passed to the MATLAB component (as part of a more generalised serialisation routine).

So far, I've been reasonably successful with passing tables of numeric data but I've hit a snag when trying to add a column of datatype DateTime. What I've been doing up to now is stuffing the values from the DataTable into a double array, because MATLAB only really cares about doubles, and then doing a straight cast to a MWNumericArray, which is essentially a matrix.

Here's the current code;

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 is my own routine which caters for NULLS, DBNull and returns double.NaN, again because Matlab treats all NULLS as NaNs.

So here's the thing; Does anyone know of a MATLAB datatype that would allow me to pass in a contiguous array with multiple datatypes? The only workaround I can conceive of involves using a MWStructArray of MWStructArrays, but that seems hacky and I'm not sure how well it would work in the MATLAB code, so I'd like to try to find a more elegant solution if I can. I've had a look at using an MWCellArray, but it gives me a compile error when I try to instantiate it.

I'd like to be able to do something like;

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

But as I said, I get a compile error with this, also with passing an object array to the constructor.

Apologies if I have missed anything silly. I've done some Googling, but information on Matlab to .NET interfaces seems a little light, so that is why I posted it here.

Thanks in advance.

[EDIT]

Thanks to everyone for the suggestions.

Turns out that the quickest and most efficient way for our specific implementation was to convert the Datetime to an int in the SQL code.

However, of the other approaches, I would recommend using the MWCharArray approach. It uses the least fuss, and it turns out I was just doing it wrong - you can't treat it like another MWArray type, as it is of course designed to deal with multiple datatypes you need to iterate over it, sticking in MWNumerics or whatever takes your fancy as you go. One thing to be aware of is that MWArrays are 1-based, not 0-based. That one keeps catching me out.

I'll go into a more detailed discussion later today when I have the time, but right now I don't. Thanks everyone once more for your help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

何以畏孤独 2024-12-13 03:32:16

正如 @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 中,System.DateTime 将日期和时间表示为:

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

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

日期序列号代表整数和小数
从特定日期和时间算起的天数,其中 datenum('Jan-1-0000
00:00:00') 返回数字 1

为此,我在 C# 应用程序中编写了两个辅助函数来转换 DateTime“刻度”以匹配序列日期数字的 MATLAB 定义。


首先,考虑这个简单的 MATLAB 函数。它期望接收一个包含表数据的 numRos-by-numCols 元胞数组。在我的示例中,列是:名称(字符串)、价格(双精度)、日期(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

使用 deploytool 来自 MATLAB Builder NE,我们将上述内容构建为 .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

As @Matt suggested in the comments, if you want to store different datatypes (numeric, strings, structs, etc...), you should use the equivalent of cell-arrays exposed by this managed API, namely the MWCellArray class.

To illustrate, I implemented a simple .NET assembly. It exposes a MATLAB function that receives a cell-array (records from a database table), and simply prints them. This function would be called from our C# application, which generates a sample DataTable, and convert it into a MWCellArray (fill table entries cell-by-cell).

The trick is to map the objects contained in the DataTable to the supported types by the MWArray-derived classes. Here are the ones I used (check the documentation for a complete list):

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

A note about the date/time data: in .NET, the System.DateTime expresses date and time as:

the number of 100-nanosecond intervals that have elapsed since January
1, 0001 at 00:00:00.000

while in MATLAB, this is what the DATENUM function has to say:

A serial date number represents the whole and fractional number of
days from a specific date and time, where datenum('Jan-1-0000
00:00:00') returns the number 1

For this reason, I wrote two helper functions in the C# application to convert the DateTime "ticks" to match the MATLAB definition of serial date numbers.


First, consider this simple MATLAB function. It expects to receive a numRos-by-numCols cellarray containing the table data. In my example, the columns are: 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

Using deploytool from MATLAB Builder NE, we build the above as a .NET assembly. Next, we create a C# console application, then add a reference to the MWArray.dll assembly, in addition to the above generated one. This is the program I am using:

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;
        }
    }
}

The output of this C# program as returned by the compiled MATLAB function:

'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
嘴硬脾气大 2024-12-13 03:32:16

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

详细信息在这里。
http://www.mathworks.com/help/matlab/ref/net .add assembly.html

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

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

One option, is to just open up .NET code directly from matlab, and have matlab query the database directly, using your .net interface instead of trying to go through this serialization process you describe. I have done this repeatedly in our environment with great success. In such an an endeavor
Net.addAssembly is your biggest friend.

Details are here.
http://www.mathworks.com/help/matlab/ref/net.addassembly.html

A second option would be to go with Matlab Cell Array's. You can set it up, so the columns are different data types, each column forming a cell. That is a trick matlab itself uses in the textscan function. I'd recommend reading the documentation for that function here:
http://www.mathworks.com/help/techdoc/ref/textscan.html

A third option, is to use textscan completely. Write a text file out from your .net code, and let textscan handle the parsing of it. Textscan is very powerful mechanism for getting this kind of data into matlab. You can point textscan to a file, or to a bunch of strings.

白况 2024-12-13 03:32:16

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

我尝试的是:

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

几年来 1 Jan 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 日尝试时,一切正常。有什么建议或者为什么会发生这种情况?

I have tried the functions written by @Amro but the result for certain dates are not correct.

What I tried was:

  1. Create a date in C#
  2. Use function to convert to Matlab date num as supplied by @Amro
  3. Use that number in Matlab to check its correctness

It seems to have problems with date with 1 Jan 00:00:00 for some years e.g. 2014, 2015. For example,

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

I got dtmat = 735599.0 from this.
I used in Matlab as follow:

datestr(datenum(735599.0))

I got this in return:

ans = 31-Dec-2013

When I tried 1 Jan 2012 it was OK. Any suggestion or why this happens?

纸短情长 2024-12-13 03:32:16

我和@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);
}

I had the same issue as @Johan.
The problem is in Leap years that not calculate correctly the date

To fix it I change the code that converts the DateTime to the following:

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文