我有一个函数需要 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
每列的变量都会更改,并且不能重复使用。或者,如果我不需要计算标准差(是的,只需要计算平均值),那么这可能是好的设计,并且我需要这些方法的重载版本吗?
最佳答案
您可以使用有意义的迭代器
columnName
而不是无意义的i,将
.for
循环替换为foreach
循环或者,您可以用
Select
替换循环。- 我将列选择逻辑放入该循环中,将其与计算分开。
如果您使用以下标识,则无需将平均值传递给
StdDev
函数:
你的循环变成:
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/