У меня есть большая матрица (30000 x 500), столбец представляет почасовые данные в течение следующих 3 лет, каждый столбец - это другой сценарий, т.е. У меня 500 сценариев цен, где каждая ячейка в строке имеет одну и ту же метку времени.
Мне нужно агрегировать это по оси времени, поэтому, если ежедневно мне нужно создать матрицу (30000/nrdays x 500), если ежемесячно (30000/nrmonths x 500) и, очевидно, также сохранить правильные даты.
В Matlab я создал индекс с уникальным номером для каждого дня или каждого месяца, а затем зациклился на столбцах, используя это:
accumarray(idx,price(:,i),[numel(unique(idx)) 1], @mean)
Если я хочу сделать это в С#, что лучший способ?
ниже - то, что у меня есть до сих пор:
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? Моя последняя строка выше не работает.
ОБНОВИТЬ:
Я добавил более настоятельную версию без 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 работала.
Обновление 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, но я сомневаюсь, что она будет быстрее.
Ваша версия обновления соответствует тому, как массивы должны обрабатываться в 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>
полученный ILArray<double>
и ILArray<double>
его строки (а не через строки origmatrix
) - ровно один раз. Количество результирующих строк должно быть известно заранее, правильно? По крайней мере, если строки дат представляют даты/время, вы можете вычислить начало и конец указателя в соответствии с текущей строкой в tempmatrix и использовать это, как и вы.
3) Используйте ILMath.full
или ":"
чтобы указать полное измерение в выражении подмассива.
4) Используйте правила функции ILNumerics ! Ваши матрицы достаточно велики, так что это, скорее всего, даст некоторое ускорение из-за пула памяти и более эффективного распараллеливания.
5) Формулирование вашей функции в классе, полученном из ILMath, приводит к значительно более сильному синтаксису, позволяя вам опустить ILMath.
идентификатор для всех выражений, таких как ILMath.r(...
, ILMath.mean
.
Кроме того, некоторые правила, которые следует учитывать при использовании ILArray в качестве атрибутов класса (как в первом примере), см. По адресу: http://ilnumerics.net/ClassRules.html