[英]Most efficient way of matrix aggregation in c# and ILNumerics vs Matlab
我有一个大矩阵(30000 x 500),一列表示未来3年的每小时数据,每列是不同的情况,即我有500个价格情况,其中行中的每个单元格都具有相同的时间戳。
我需要在时间轴上进行汇总,因此如果每天我都需要制作一个(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? 我上面的最后一行不起作用。
更新:
我添加了一个不带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>
并遍历其行(而不是遍历origmatrix
的行)-恰好一次 。 结果行数应该事先知道,对吧? 至少如果日期行表示日期/时间,则可以根据tempmatrix中的当前行计算开始和结束指示符,并像以前一样使用它们。
3)使用ILMath.full
或":"
表示子ILMath.full
表达式中的完整尺寸。
4)使用ILNumerics函数规则 ! 您的矩阵足够大,因此由于内存池和更有效的并行化,很有可能会加快速度。
5)将函数表示在从ILMath派生的类中,可以通过省略ILMath来带来更好的语法ILMath.
所有表达式(如ILMath.r(...
, ILMath.mean
。
另外,将ILArray用作类属性时要记住的一些规则(如您的第一个示例)请参见: http : //ilnumerics.net/ClassRules.html
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.