Передача данных .NET в MATLAB - PullRequest
       0

Передача данных .NET в MATLAB

1 голос
/ 23 сентября 2011

Я создаю интерфейсный слой для компонента 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, который позволил бы мне передавать непрерывный массив с несколькими типами данных? Единственный обходной путь, который я могу придумать, заключается в использовании MWStructArray из MWStructArrays, но это кажется хакерским, и я не уверен, насколько хорошо это будет работать в коде MATLAB, поэтому я хотел бы попытаться найти более элегантный Решение, если я могу. Я рассмотрел использование MWCellArray, но при попытке его создания я получаю ошибку компиляции.

Я хотел бы иметь возможность сделать что-то вроде;

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

Но, как я уже сказал, я получаю ошибку компиляции, в том числе и при передаче массива объектов в конструктор.

Извиняюсь, если я что-то глупо пропустил. Я немного погуглил, но информация о интерфейсах Matlab to .NET кажется немного легкой, поэтому я разместил ее здесь.

Заранее спасибо.

[EDIT]

Спасибо всем за предложения.

Оказывается, что самым быстрым и эффективным способом для нашей конкретной реализации было преобразование Datetime в int в коде SQL.

Однако из других подходов я бы рекомендовал использовать подход MWCharArray. Он использует наименьшее количество суеты, и оказывается, что я просто делал это неправильно - вы не можете относиться к нему как к другому типу MWArray, поскольку он, конечно, предназначен для работы с несколькими типами данных, которые вам нужно перебирать, вставляя в MWNumerics все, что вам нравится на ходу. Следует помнить, что MWArrays основаны на 1, а не на 0. Тот продолжает меня преследовать.

Я более подробно остановлюсь сегодня, когда у меня будет время, а сейчас нет. Спасибо всем еще раз за вашу помощь.

Ответы [ 3 ]

6 голосов
/ 26 сентября 2011

Как указано в комментариях @ 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 выражает дату и время как:

число интервалов в 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;
        }
    }
}

Вывод этой программы на 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
2 голосов
/ 23 сентября 2011

Один из вариантов - просто открыть .NET-код непосредственно из matlab и заставить 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

Третий вариант - полностью использовать текстовое сканирование. Напишите текстовый файл из вашего кода .net, и пусть тексты могут обработать его. Textscan - очень мощный механизм для передачи данных такого рода в Matlab. Вы можете указать текстовое сканирование на файл или набор строк.

0 голосов
/ 05 августа 2015

Я пробовал функции, написанные @Amro, но результаты для определенных дат не верны.

То, что я пробовал, было:

  1. Создать дату в C #
  2. Используйте функцию для преобразования в Matlab дату-число, предоставленную @ Amro
  3. Используйте это число в Matlab, чтобы проверить его правильность

Кажется, в течение нескольких лет возникают проблемы с датой с 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

Когда я попробовал 1 января 2012 года, все было в порядке. Любое предложение или почему это происходит?

...