C#回归线绘制结果与Python偏差,请求排查问题原因
问题排查:C#回归代码偏差原因分析
Python代码生成的回归线图表能保留刻度比例,回归线贴合更多数据点,准确性更高;而C#代码生成的图表结果存在明显偏差。以下是两段实现代码,我们来排查C#代码的问题:
Python代码
# Fit2.py import numpy as np def ReadDataFromFile(data_file_path): """ Reads data from a file assuming two columns of numbers, separated by whitespace. Returns two lists, one for x-values and one for y-values. """ with open(data_file_path, 'r') as file: lines = file.readlines() x_values = [] y_values = [] for line in lines: parts = line.strip().split() if len(parts) == 2: x_values.append(float(parts[0])) y_values.append(float(parts[1])) return x_values, y_values def CreateRegressionLine(x_values, y_values): """ Creates a regression line based on log-log transformed data. Returns two lists containing the log-space points of the regression line. """ log_x_values = np.log(x_values) log_y_values = np.log(y_values) # Fit the regression using np.polyfit for a first degree polynomial, which is a linear fit slope, intercept = np.polyfit(log_x_values, log_y_values, 1) # Create the regression line in log space regression_log_y_values = slope * log_x_values + intercept # Convert the regression line back to linear space regression_y_values = np.exp(regression_log_y_values) return x_values, regression_y_values.tolist()
C#代码
public static class Fit2 { public static Tuple<List<double>, List<double>> ReadDataFromFile(string filePath) { var xList = new List<double>(); var yList = new List<double>(); foreach (var line in File.ReadAllLines(filePath)) { if (line.StartsWith("#")) continue; // Skip the comment lines var parts = line.Split('\t'); if (parts.Length == 2) { if (double.TryParse(parts[0], out var x) && double.TryParse(parts[1], out var y)) { xList.Add(x); yList.Add(y); } } } return Tuple.Create(xList, yList); } public static Tuple<double, double> CalculateLinearRegressionCoefficients(double[] xValues, double[] yValues) { if (xValues.Length != yValues.Length) { throw new ArgumentException("Input arrays must have the same length."); } var (a, b) = SimpleRegression.Fit(xValues, yValues); return Tuple.Create(a, b); } public static Tuple<List<double>, List<double>> CreateRegressionLine(List<double> xList, List<double> yList) { var xValues = xList.ToArray(); var yValues = yList.ToArray(); var coefficients = CalculateLinearRegressionCoefficients(xValues, yValues); var xVals = new List<double> { xValues.First(), xValues.Last() }; var yVals = xVals.Select(x => coefficients.Item1 + coefficients.Item2 * x).ToList(); return Tuple.Create(xVals, yVals); } }
核心问题分析
对比两段代码的实现逻辑,C#代码存在三个关键错误:
缺少对数转换步骤
Python实现的是对数-对数空间的线性回归(用于拟合幂律关系):先对x、y取自然对数,在对数空间拟合直线,再通过指数转换回原线性空间,这是处理这类数据的标准方法。而C#直接对原始x、y值做线性回归,完全跳过了对数转换,这是导致回归线偏差的核心原因。回归线生成方式错误
Python用所有输入的x值计算对应的回归y值,生成的是贴合全量数据的曲线;C#仅取x的首尾两个点计算y值,画的是两点直线,即使回归逻辑正确,也无法体现数据的整体趋势,更无法贴合中间数据点。数据读取的潜在兼容性问题
Python用任意空白字符分割数据(空格、制表符都支持),而C#仅用制表符\t分割。如果数据文件是用空格分隔的,C#会读取不到有效数据,进一步导致回归结果错误。
修正后的C#代码示例
using System; using System.Collections.Generic; using System.Linq; using MathNet.Numerics.Statistics; // 需要引用MathNet.Numerics库 public static class Fit2 { public static Tuple<List<double>, List<double>> ReadDataFromFile(string filePath) { var xList = new List<double>(); var yList = new List<double>(); foreach (var line in File.ReadAllLines(filePath)) { if (line.StartsWith("#")) continue; // 改用任意空白分割,和Python保持一致 var parts = line.Trim().Split(new[] {' ', '\t'}, StringSplitOptions.RemoveEmptyEntries); if (parts.Length == 2) { if (double.TryParse(parts[0], out var x) && double.TryParse(parts[1], out var y)) { xList.Add(x); yList.Add(y); } } } return Tuple.Create(xList, yList); } public static Tuple<double, double> CalculateLogLogRegressionCoefficients(List<double> xList, List<double> yList) { if (xList.Count != yList.Count) { throw new ArgumentException("Input lists must have the same length."); } // 对x、y取自然对数 var logX = xList.Select(Math.Log).ToArray(); var logY = yList.Select(Math.Log).ToArray(); // 在对数空间拟合线性回归 var regression = SimpleRegression.Fit(logX, logY); // regression.Item1是截距,Item2是斜率 return Tuple.Create(regression.Item1, regression.Item2); } public static Tuple<List<double>, List<double>> CreateRegressionLine(List<double> xList, List<double> yList) { var coefficients = CalculateLogLogRegressionCoefficients(xList, yList); var intercept = coefficients.Item1; var slope = coefficients.Item2; // 用所有x值计算回归y值(转换回原空间) var regressionY = xList.Select(x => Math.Exp(intercept + slope * Math.Log(x))).ToList(); return Tuple.Create(xList, regressionY); } }
内容的提问来源于stack exchange,提问作者user366312
相关产品推荐
相关产品推荐

