c# - 重构 LINQ 方法

标签 c# linq

我有一个函数需要 DataTable作为参数并返回 NormalData 类型的对象对于数据表中的每一列 普通数据定义

public class NormalData
{
    //AttributeName = ColumnName of DataTable
    public string AttributeName { get; set; }

    //each column will have its mean and standard deviation computed
    public double Mean { get; set; }
    public double StandardDeviation { get; set; }

    //a DataTable with three columns will create an IEnumerable<NormalData>
    //with a count of three
}

以下内容有效,但我想了解我如何实现它的第二个意见:

public static IEnumerable<NormalData>  GetNormalDataByTableColumns(DataTable dt)
{
    //get list of column names to iterate over
    List<string> columnList = GetDataTableColumnNames(dt);
    List<NormalData> normalDataList = new List<NormalData>();
    for (int i = 0; i < columnList.Count; i++)
    {
        //creates a NormalData object for each column in the DataTable
        NormalData normalData = new NormalData();

        //find average
        normalData.Mean = GetColumnAverage(dt, columnList[i]);

        //find stDev
        normalData.StandardDeviation = GetColumnStDev(dt,columnList[i],normalData.Mean);
        normalData.AttributeName = columnList[i];

        //add to NormalDataList
        normalDataList.Add(normalData);
    }

    return normalDataList;
}

private static List<string> GetDataTableColumnNames(DataTable dt)
{
    return (from DataColumn dc in dt.Columns
        select dc.ColumnName).ToList();
}

private static double GetColumnAverage(DataTable dt, string columnName)
{
    return dt.AsEnumerable().Average(x => x.Field<double>(columnName));
}

private static double GetColumnStDev(DataTable dt, string columnName,double average)
{
    var squaredDiffs = (dt.AsEnumerable()
        .Sum(x => (x.Field<double>(columnName) - average) *
        x.Field<double>(columnName) - average));

    return Math.Sqrt(squaredDiffs / dt.Rows.Count);
}

我觉得设计不好的是GetColumnAverage的参数列表和GetColumnStDev都需要采取。实际上,它们应该只需要一个数字类型列表(不一定是 double,但目前是硬编码的)来计算它们的值。然而,这是我今天早上让它发挥作用的唯一方法。我在此设计中违反了哪些规则?我怎样才能修改这个,以便 GetColumn..函数仅采用 DataColumn for 中对此进行了迭代columnList的循环?

编辑:average每列的变量都会更改,并且不能重复使用。或者,如果我不需要计算标准差(是的,只需要计算平均值),那么这可能是好的设计,并且我需要这些方法的重载版本吗?

最佳答案

  1. 您可以使用有意义的迭代器 columnName 而不是无意义的 i,将 for 循环替换为 foreach 循环.

    或者,您可以用 Select 替换循环。

  2. 我将列选择逻辑放入该循环中,将其与计算分开。
  3. 如果您使用以下标识,则无需将平均值传递给 StdDev 函数:

    (来自 http://en.wikipedia.org/wiki/Standard_deviation )

你的循环变成:

foreach (string columnName in columnList)
{
    var columnData = dt.AsEnumerable().Select(x => x.Field<double>(columnName));

    //creates a NormalData object for each column in the DataTable
    NormalData normalData = new NormalData();
    normalData.Mean = columnData.Average();
    normalData.StandardDeviation = StdDev(columnData);
    normalData.AttributeName = columnName;

    //add to NormalDataList
    normalDataList.Add(normalData);
}

使用辅助方法:

public static double StdDev(IEnumerable<double> seq)
{
    long count = 0;
    double sum = 0;
    double sumOfSquares = 0;
    foreach(var value in seq)
    {
        sum += value;
        sumOfSquares += value * value;
        count++;
    }
    double average = sum / count;
    double averageSquare = sumOfSquares / count;
    return Math.Sqrt(averageSquare - average * average);
}

关于c# - 重构 LINQ 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20640586/

相关文章:

c# - 如何从 double 转换为 Int64

linq - Azure 函数 CosmosDB 查询序列化

c# - 如何动态构建 () => x.prop lambda 表达式?

c# - 找不到 ExpressionVisitor.VisistMemberAcess

c# - LINQ 首先选择

C# Richtextbox - 通过滚动并按住 ctrl 更改文本大小后获取文本大小

c# - Umbraco 新手,我应该从哪里开始?

c# - 在 C# 中,如何确定文件是二进制文件还是文本文件?

c# - 通过 Web API 中的属性限制 OData $filter

linq - 如何使用 LINQ 获取唯一 id 列的最大值