【问题标题】:Calculating an array in LINQ C#在 LINQ C# 中计算数组
【发布时间】:2015-07-09 15:03:34
【问题描述】:

我想用 linq 用一个短语计算相关矩阵。我该怎么做(如果可能的话)?

假设我已经有一个名为 volatilites 的大小为 N 的数组,而 Returns 是一个锯齿状数组,其中 N 个数组的大小都相同。

我也在用:

using stats = MathNet.Numerics.Statistics.ArrayStatistics

这是我想在 LINQ 中编写的代码:

double[,] correlation_matrix = new double[N,N];
for (int i=0; i<N;i++){
    for (int j = i + 1; j < N; j++){
        correlation_matrix [i,j]= stats.Covariance(Returns[i], Returns[j]) / (volatilities[i] * volatilities[j]); // stores it to check values       
    }
}

谢谢!

【问题讨论】:

  • 我不相信有任何 linq 运算符会创建多维数组,所以我认为您不能将其作为 LINQ 单行来执行。
  • 这种简单的方法(for循环)有什么问题?
  • Linq 并不总是您认为的灵丹妙药。如果它有效并且可读,那就别管它了:)
  • @Chris 您可以使用Aggregate 将二维数组填充到 LINQ-fy 中,将该代码转换为单个语句,但我怀疑有人会认为这是一种改进。
  • 我同意其他人的观点。您当前的方法非常清楚它的作用。我相信您可以编写一些 LINQy 来执行此操作,但不会比这更清楚了。

标签: c# arrays linq


【解决方案1】:

如果你让自己有一个数组,你可以这样做

var correlation_matrix = 
    Returns.Select((r_i, i) => 
        Returns.Where((r_j, j) => j > i).Select((r_j, j) =>
            stats.Covariance(r_i, r_j) / (volatilities[i] * volatilities[j])
        ).ToArray()
    ).ToArray();

如果您想使用范围(根据您的评论),您可以这样做

var N = Returns.Length;
var correlation_matrix = 
    Enumerable.Range(0, N).Select(i => 
        Enumerable.Range(i + 1, N - i - 1).Select(j =>
            stats.Covariance(Returns[i], Returns[j]) / (volatilities[i] * volatilities[j])
        ).ToArray()
    ).ToArray();    

这并不是说你应该这样做。循环版本更具可读性和性能。

【讨论】:

  • 我几乎不愿意为此 +1,因为正如您所说,原版要好得多。但是您确实说原版更好,所以+1。 :)
  • 谢谢@Jerry!如果一个版本需要从 0 到 N-1 的两个范围,即 {0,1,2,3,... N-1} 并做到这一点,怎么样?不是更易读吗?
  • @Escachator 请注意,与您的原始示例相比,此代码存在差异。不知道哪一个是正确的。
  • Alexei 是对的——我已经更正了内部 Enumerable.Range 的计数。
【解决方案2】:

每个 OP 请求 Enumerable.Aggregate 版本,结果为二维数组:

var correlation_matrix = 
   Enumerable.Range(0, N).Select(i => 
       Enumerable.Range(i + 1, N - i - 1).Select(j => 
         new {
            i, j, // capture location of the result
            Data = i + j } // compute whatever you need here
       )
   )
   .SelectMany(r => r) // flatten into single list
   .Aggregate(
       new double[N,N], 
       (result, item) => { 
           result[item.i, item.j] = item.Data; // use pos captured earlier
           return result; // to match signature required by Aggregate
        });

旁注:这本质上是使用 LINQ 的练习,而不是您应该在实际代码中使用的代码。

  • 代码必须将位置捕获到匿名对象中,导致大量不必要的分配
  • 我认为这个版本比普通的for 版本更难阅读

【讨论】:

    猜你喜欢
    • 2016-01-31
    • 1970-01-01
    • 1970-01-01
    • 2021-01-19
    • 2021-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-04
    相关资源
    最近更新 更多