1

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

到目前为止,我在传递数字数据表方面相当成功,但是在尝试添加 datatype 列时遇到了障碍DateTime。到目前为止,我一直在做的是将值从 填充DataTable到双精度数组中,因为 MATLAB 只真正关心双精度数,然后直接转换为 a 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 数据类型可以让我传入具有多种数据类型的连续数组?我能想到的唯一解决方法是使用MWStructArrayof MWStructArrays,但这似乎很老套,我不确定它在 MATLAB 代码中的效果如何,所以如果可以的话,我想尝试找到一个更优雅的解决方案。我看过使用MWCellArray,但是当我尝试实例化它时它给了我一个编译错误。

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

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

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

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

提前致谢。

[编辑]

感谢大家的建议。

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

但是,在其他方法中,我建议使用 MWCharArray 方法。它使用最少的大惊小怪,事实证明我只是做错了 - 你不能把它当作另一种 MWArray 类型,因为它当然设计用于处理需要迭代它的多种数据类型,坚持使用 MWNumerics 或随心所欲,随心所欲。需要注意的一件事是 MWArrays 是基于 1 的,而不是基于 0 的。那个人一直在抓我。

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

4

4 回答 4

6

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

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

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

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

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


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

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

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

'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
于 2011-09-26T17:25:50.870 回答
2

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

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

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

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

于 2011-09-23T16:11:21.860 回答
0

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

我尝试的是:

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

在某些年份,例如 2014 年、2015 年,日期似乎有问题 1 Jan 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 日尝试时,一切正常。任何建议或为什么会发生这种情况?

于 2015-08-05T12:12:14.837 回答
0

我和@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);
}
于 2019-09-20T13:49:10.007 回答