【问题标题】:Is there a way to simplify this with a loop or linq statement?有没有办法用循环或 linq 语句来简化它?
【发布时间】:2017-09-12 00:16:32
【问题描述】:

我正在尝试找出是否有办法为下面的示例代码创建循环

// the objects below create a list of decimals
var ema12 = calc.ListCalculationData.Select(i => (double)i.Ema12);
var ema26 = calc.ListCalculationData.Select(i => (double)i.Ema26);
var ema = calc.ListCalculationData.Select(i => (double)i.Ema);
var adl = calc.ListCalculationData.Select(i => (double)i.ADL);

var r1 = GoodnessOfFit.RSquared(ema12);
var r2 = GoodnessOfFit.RSquared(ema26);
var r3 = GoodnessOfFit.RSquared(ema);
var r4 = GoodnessOfFit.RSquared(adl);

我正在尝试获得类似于以下伪代码的内容。请记住,每个 var 项都是小数列表

foreach (var item in calc.ListCalculationData.AsEnumerable())
{
    var item2 = calc.ListCalculationData.Select(i => (double)item);
    var r1 = GoodnessOfFit.RSquared(item2);
}

更多信息:

ListCalculationData 是我在下面添加的自定义类的列表。我想要做的是循环遍历该类中的每个变量并执行选择查询以对选择查询返回的小数列表执行拟合优度 rsquared 计算,因此它简化了我的代码并使其类似于我的伪代码

public class CalculationData
    {
        public decimal Ema { get; set; }
        public decimal Ema12 { get; set; }
        public decimal Ema26 { get; set; }
        public decimal ADL { get; set; }
    }

更新:我为本地函数尝试了这个,但它失败了 ;预期的和无效的{

double r(Func<CalculationData, double> f) =>
{ GoodnessOfFit.RSquared(calc.ListCalculationData.Select(f), vectorArray) };

更新 2:由于建议,这是我当前代码设置的内容,但显然这不起作用,因为它表示名称 i 在此部分的上下文中不存在:nameof(i.Ema12 ) 也因为我使用的主要是伪代码

 MultipleRegressionInfo rn(Func<CalculationData, double> f, string name, int days)
                            {

 MultipleRegressionInfo mrInfo = new MultipleRegressionInfo
                            {
                                RSquaredValue = GoodnessOfFit.RSquared(calc.ListCalculationData.Select(f), vectorArray),
                                ListValues = (List<double>)calc.ListCalculationData.Select(f).ToList(),
                                ValueName = name,
                                Days = days
                            };
                            listMRInfo.Add(mrInfo);

                            return mrInfo;
                        };

  MultipleRegressionInfo rnList(Func<CalculationData, List<decimal>> f, string name, int days)
                            {

 MultipleRegressionInfo mrInfo = new MultipleRegressionInfo
                            {
                                RSquaredValue = GoodnessOfFit.RSquared(calc.ListCalculationData.Select(f), vectorArray),
                                ListValues = (List<double>)calc.ListCalculationData.Select(f).ToList(),
                                ValueName = name,
                                Days = days
                            };
                            listMRInfo.Add(mrInfo);

                            return mrInfo;
                        };

  foreach (CalculationData calc in ListCalculationData)
  {
     foreach (object value in calc)
     {
           if (value == typeof(decimal))
           {
                MultipleRegressionInfo r1 = rn(i => (double)i.value, nameof(i.value), 100)
           }
           else if (value == typeof(List<decimal>)
           {
                MultipleRegressionInfo r1 = rnList(i => i.value, nameof(i.value), 100)
           }
     }
  }

【问题讨论】:

  • 没有更多上下文,您的问题太宽泛了。单个 Select() 可以返回元组枚举而不是单个值。但是您仍然需要多次调用RSquared(),并使用Select() there 再次提取元组值。我不认为这有多大帮助。在您的循环示例中,您将采用另一种方式,但您需要反射或预填充的访问器委托数组来获取您尝试获取的属性/字段值。如果您能提供一个好的minimal reproducible example 并更好地解释您尝试过的内容以及您遇到的具体问题,将会有所帮助。
  • @PeterDuniho 我添加了更多信息,希望能为您澄清事情
  • 问题变得越来越混乱。你能看看例如dotnetfiddle.net/Sb65DZ,看看它对你是否有意义?

标签: c# linq


【解决方案1】:

您可以将每个单独的字段表示为检索特定字段值的 lambda(我认为这更好),也可以表示为使用反射来实现相同目的的字符串或 PropertyType 值。

    var getters = new Func<CalculationData, double>[] {
        (i) => (double)i.Ema12,
        (i) => (double)i.Ema26,
        (i) => (double)i.Ema,
        (i) => (double)i.ADL,
    };

然后,只需获取每个单独的IEnumerable&lt;double&gt; 序列并计算其 R 平方值即可。

    var dataseries = getters.Select((func) => calc.ListCalculationData.Select(func));
    double[] results = dataseries.Select((data) => GoodnessOfFit.RSquared(data)).ToArray();


来自 cmets:

这与我正在寻找的内容相似,但我的班级中有 40 多个变量,我添加了更多信息以尝试解释我正在尝试做的事情,但我试图阻止额外的 40 行代码来做类似于你的代码的事情

以下应该使用反射来满足您的要求。

 IEnumerable<Func<CalculationData, double>> getters =
     typeof(CalculationData).GetProperties()
     .Select<PropertyInfo, Func<CalculationData, double>>(
         (PropertyInfo p) => (CalculationData x) => (double)(decimal)p.GetValue(x)
     );

编辑:问题再次被编辑,我不再确定你需要 getter 的间接性。请参阅https://dotnetfiddle.net/Sb65DZ 了解我如何编写此代码的简单示例。

【讨论】:

  • 这与我正在寻找的类似,但我的班级中有 40 多个变量,我添加了更多信息以尝试解释我正在尝试做的事情,但我试图阻止额外的 40 行代码来做类似于你的代码的事情
  • @user3610374:您可以使用反射和Expression 类来动态生成上述答案使用的委托实例。生成代表将有一次性的前期成本,但一旦完成,计算将与上述一样有效。
  • @PeterDuniho 恐怕我对此并不熟悉。你有一个简短的例子来说明你在说什么或参考文章吗?
  • 你可以搜索 Stack Overflow。但这里有几个相关的例子:stackoverflow.com/questions/40263509/…stackoverflow.com/questions/5075484/…。这些示例使用 lambdas 来填充表达式;您需要使用反射来获取表达式树中使用的PropertyInfo 对象。但基本思想是一样的。
  • @user3610374 我添加了一个示例,说明如何使用反射来执行此操作。与 peterduniho 建议的动态编译委托相比,每次运行都会产生一些反射开销,但我通常声称 YAGNI 可以进行更复杂的元编程,直到基准测试证明并非如此。
【解决方案2】:

在 Visual Studio 2015+ 中,您可以使用 local functions(未测试):

double r(Func<CalculationData, double> f) => 
                               GoodnessOfFit.RSquared(calc.ListCalculationData.Select(f)); 

double r1 = r(i => (double)i.Ema12), r2 = r(i => (double)i.Ema26), 
       r3 = r(i => (double)i.Ema)  , r4 = r(i => (double)i.ADL);

或者效率低一点的 lambda:

Func<Func<CalculationData, double>, double> r = f =>                      
                               GoodnessOfFit.RSquared(calc.ListCalculationData.Select(f)); 

double r1 = r(i => (double)i.Ema12), r2 = r(i => (double)i.Ema26), 
       r3 = r(i => (double)i.Ema)  , r4 = r(i => (double)i.ADL);

另一种选择是将它们转换为数组:

Func<CalculationData, double>[] lambdas = { i => (double)i.Ema12, i => (double)i.Ema26, 
                                            i => (double)i.Ema,   i => (double)i.ADL };

double[] r = Array.ConvertAll(lambdas, f =>                     
                               GoodnessOfFit.RSquared(calc.ListCalculationData.Select(f))); 

要使用反射找到具有最大 rsquared 值的属性,您可以试试这个:

Tuple<double, string> maxR = typeof(CalculationData).GetProperties().Max(p => Tuple.Create(
    GoodnessOfFit.RSquared(calc.ListCalculationData.Select(i => Convert.ToDouble(p.GetValue(i)))), p.Name));

double maxRvalue = maxR.Item1;
string maxRname = maxR.Item2;

【讨论】:

  • 我喜欢数组替代方案,但有没有一种方法可以查看哪个变量返回最高的 goodnessoffit rsquared 值?
  • @user3610374 数组替代方案与其他答案几乎相同。您可以尝试反射示例,但请记住,反射比使用 lambda 为每个属性创建数组要慢得多(不确定多少)。
  • 我更喜欢数组替代方案,但我需要一种方法来找出哪个变量的值最高
  • 你能在本地函数中添加更多的行吗?
  • @user3610374 当然,如果你像常规方法一样使用{}
【解决方案3】:

您可以使用扩展方法来收集共同的操作序列。

public static class CalculationDataExtensions
{
  public static IEnumerable<double> CalcRSquared(
    this IEnumerable<CalculationData> source,
    Func<CalculationData, decimal> propertySelector)
  {
    IEnumerable<double> values = source
      .Select(propertySelector)
      .Select(x => (double)x);

    return GoodnessOfFit.RSquared(values);
  }
}

被调用

var r1 = calc.ListCalculationData.CalcRSquared(x => x.Ema12);
var r2 = calc.ListCalculationData.CalcRSquared(x => x.Ema26);
var r3 = calc.ListCalculationData.CalcRSquared(x => x.Ema);
var r4 = calc.ListCalculationData.CalcRSquared(x => x.ADL);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-27
    • 2023-03-04
    • 1970-01-01
    • 2020-01-06
    • 2015-08-13
    • 1970-01-01
    相关资源
    最近更新 更多