2014-09-10 2 views
1

У меня есть большая матрица (30000 x 500), столбец представляет почасовые данные в течение следующих 3 лет, каждый столбец - это другой сценарий, то есть у меня есть 500 сценариев цены, где каждая ячейка в строке имеет одну и ту же метку времени.Самый эффективный способ агрегации матриц в C# и ILNumerics vs Matlab

Мне нужно агрегировать это по оси времени, поэтому, если ежедневно мне нужно сделать матрицу (30000/nrdays x 500), если ежемесячно (30000/nrmonths x 500) и, очевидно, также сохранить правильные даты.

В MATLAB я создал индекс с уникальным номером на каждый день или каждый месяц, а затем петельный по столбцам с помощью этого:

accumarray(idx,price(:,i),[numel(unique(idx)) 1], @mean) 

Если я хочу сделать это в C#, что является лучшим способом?

ниже то, что я до сих пор:

public class matrixwihtdates 
    { 
     public DateTime dats; 
     public ILArray<double> nums; 
    } 
    public class endres 
    { 
     public string year; 
     public string month; 
     public string day; 
     public ILArray<double> nums; 
    } 

    public static List<endres> aggrmatrix(ILArray<double> origmatrix, DateTime std, DateTime edd) 
    { 
     var aggrmatr = new List<matrixwihtdates>(); 
     for (int i = 0; i < origmatrix.Length; i++) 
     { 
      aggrmatr.Add(new matrixwihtdates 
      { 
       dats = std.AddHours(i), 
       nums = origmatrix[i, "full"], 
      }); 
     } 

     return aggrmatr.GroupBy(a => new { yr = a.dats.Year, mt = a.dats.Month }) 
      .Select(g => new endres { 
      year = g.Key.yr.ToString(), 
      month = g.Key.mt.ToString(), 
      nums = ILMath.mean(g.Select(a => a.nums).ToArray(),1) }).ToList(); 


    } 

Основная проблема в том, что я не знаю, как в среднем по каждому из столбцов в синтаксисе LINQ, так что вектор (1x500) возвращается , Или я не должен использовать LINQ? Моя последняя строка выше не работает.

UPDATE:

Я добавил более настоятельную версию без LINQ, это, кажется, работает, но немного неуклюжий еще.

 public static List<ILArray<double>> aggrmatrixImp(ILArray<double> origmatrix, DateTime std) 
    { 
     List<ILArray<double>> aggrmatr = new List<ILArray<double>>(); 
     ILArray<double> tempmatrix; 
     int startindicator = 0; 
     int endindicator = 0; 
     int month = std.Month; 
     for (int i = 0; i < origmatrix.Length; i++) 
     { 
      if (std.AddHours(i).Month != month) 
      { 
       endindicator = i - 1; 
       tempmatrix = origmatrix[ILMath.r(startindicator, endindicator), ILMath.r(0, ILMath.end)]; 
       aggrmatr.Add(ILMath.mean(tempmatrix, 1)); 
       startindicator = i; 
       month = std.AddHours(i).Month; 
      } 
     } 
     return aggrmatr; 
    } 

Я все еще хотел бы сделать версию LINQ.

Update 2

Я принял советовать Haymo в расчет, и вот еще одна версия, которая в два раза быстрее.

public static ILArray<double> aggrmatrixImp2(ILArray<double> origmatrix, DateTime firstdateinfile, DateTime std, DateTime edd) 
    { 
     int nrmonths = ((edd.Year - std.Year) * 12) + edd.Month - std.Month; 
     ILArray<double> aggrmatr = ILMath.zeros(nrmonths,500); 
     int startindicator = std.Date.Subtract(firstdateinfile.Date).Duration().Days*24; 
     int endindicator = 0; 
     DateTime tempdate = std.AddMonths(1); 
     tempdate = new DateTime(tempdate.Year, tempdate.Month, 1); 
     for (int i = 0; i < nrmonths; i++) 
     { 
       endindicator = tempdate.Date.Subtract(std.Date).Duration().Days * 24-1; 
       aggrmatr[i, ILMath.full] = ILMath.mean(origmatrix[ILMath.r(startindicator, endindicator), ILMath.full], 1); 
       tempdate = tempdate.AddMonths(1); 
       startindicator = endindicator+1;    
     } 
     return aggrmatr; 
    } 

У меня нет рабочей версии LINQ, но я сомневаюсь, что она будет быстрее.

+0

Что было плохо, как вы пробовали? – Reniuz

+0

Я добавил свою попытку, но она не делает трюк еще – nik

+0

для простого среднего по столбцам, который можно использовать: ['ILMath.mean'] (http://ilnumerics.net/apidoc/?topic=html/M_ILNumerics_ILMath_mean_3. htm) или ['ILMath.sum'] (http://ilnumerics.net/apidoc/?topic=html/M_ILNumerics_ILMath_sum_3.htm) –

ответ

2

Ваша версия обновления соответствует тому, как массивы должны обрабатываться в ILNumerics намного лучше.

Для Linq и ILNumerics.ILArray<T>: IEnumerable<T>, который используется для перечисления ILArray<T>, выполняет итерацию по всем элементам в главном порядке столбцов. См. Здесь: http://ilnumerics.net/blog/ilnumerics-and-linq/

ILNumerics оптимизирован для актуальной, ориентированной на массив версии, которую вы используете в обновлении вопроса. Если вы решите использовать Linq в любом случае, я бы предложил агрегировать внутри оператора Linq вручную и не полагаться на ILMath.mean.

Вы могли бы попытаться оптимизировать свой второй пример из обновлений следующим образом (в случайном порядке):

1) Держите результат в виде матрицы (ILArray<double> aggrmatr) вместо List<ILArray<double>>. Вы также можете использовать ILCell, если хотите сохранить даты. Но ваш пример хранит только агрегированные числа. ILArray<double> будет поэтому достаточно.

2) предварительно выделить полученный ILArray<double> и петлю через ее строк (а не через ряды origmatrix) - точно раз. Количество результирующих строк должно быть известно заранее, правильно? По крайней мере, если строки дат представляют даты/время, вы можете вычислить начало и конец указателя в соответствии с текущей строкой в ​​tempmatrix и использовать это, как и вы.

3) Используйте ILMath.full или ":", чтобы указать полный размер в выражении подмашины.

4) Используйте ILNumerics function rules! Ваши матрицы достаточно велики, так что это, скорее всего, даст некоторое ускорение из-за пула памяти и более эффективного распараллеливания.

5) Формулирование вашей функции в классе, который является производным от ILMath приводит к гораздо приятнее синтаксису, позволяя ommit в ILMath. идентификаторе на всех выражениях, как ILMath.r(..., ILMath.mean.

Кроме того, некоторые правила, чтобы иметь в виду при использовании ILArray в качестве атрибутов класса (как в первом примере) см: http://ilnumerics.net/ClassRules.html