diff --git a/WindowsFormsApp6/Form1.cs b/WindowsFormsApp6/Form1.cs index dcedfc6..90f7efc 100644 --- a/WindowsFormsApp6/Form1.cs +++ b/WindowsFormsApp6/Form1.cs @@ -378,25 +378,36 @@ namespace WindowsFormsApp6 return; // 计算并添加/更新平均值行 - var averages = CalculateAverages(); - UpdateAverageRow(averages); + UpdateAverageRow(null); // 刷新界面 RefreshDataGridView(); } /// - /// 计算各试样的平均值 + /// 计算所有试样的总平均值 /// - private double[] CalculateAverages() + private double CalculateOverallAverage() { - var averages = new double[currentSampleCount]; - for (int i = 1; i <= currentSampleCount; i++) + var timeRows = sampleDataTable.AsEnumerable() + .Where(r => r.Field("序号") == TIME_ROW_LABEL); + + if (!timeRows.Any()) + return 0; + + double sum = 0; + int count = 0; + + foreach (var row in timeRows) { - averages[i - 1] = sampleDataTable.AsEnumerable() - .Average(r => r.Field($"试样{i}")); + for (int i = 1; i <= currentSampleCount; i++) + { + sum += row.Field($"试样{i}"); + count++; + } } - return averages; + + return count > 0 ? sum / count : 0; } /// @@ -406,23 +417,35 @@ namespace WindowsFormsApp6 { var avgRows = sampleDataTable.Select($"序号 = '{AVG_ROW_LABEL}'"); + // 计算总平均值 + double overallAvg = CalculateOverallAverage(); + if (avgRows.Length == 0) { // 添加新的平均值行 DataRow avgRow = sampleDataTable.NewRow(); avgRow["序号"] = AVG_ROW_LABEL; - for (int i = 0; i < currentSampleCount; i++) + + // 只在第一列显示总平均值 + avgRow["试样1"] = overallAvg; + + // 其他列设置为空 + for (int i = 2; i <= currentSampleCount; i++) { - avgRow[$"试样{i + 1}"] = averages[i]; + avgRow[$"试样{i}"] = DBNull.Value; } + sampleDataTable.Rows.Add(avgRow); } else { // 更新现有平均值行 - for (int i = 0; i < currentSampleCount; i++) + avgRows[0]["试样1"] = overallAvg; + + // 其他列设置为空 + for (int i = 2; i <= currentSampleCount; i++) { - avgRows[0][$"试样{i + 1}"] = averages[i]; + avgRows[0][$"试样{i}"] = DBNull.Value; } } } @@ -580,25 +603,24 @@ namespace WindowsFormsApp6 int rowIndex = sheet.LastRowNum + 1; IRow avgRow = sheet.CreateRow(rowIndex); - avgRow.CreateCell(0).SetCellValue(AVG_ROW_LABEL); + + // 序号列 + ICell labelCell = avgRow.CreateCell(0); + labelCell.SetCellValue(AVG_ROW_LABEL); + labelCell.CellStyle = avgStyle; - double[] averages = new double[currentSampleCount]; - for (int i = 1; i <= currentSampleCount; i++) + // 计算总平均值 + double overallAvg = CalculateOverallAverage(); + + // 在第一个试样列显示总平均值 + ICell avgCell = avgRow.CreateCell(1); + avgCell.SetCellValue(overallAvg); + avgCell.CellStyle = avgStyle; + + // 合并所有试样列 + if (currentSampleCount > 1) { - averages[i - 1] = dataRows.Average(r => r.Field($"试样{i}")); - avgRow.CreateCell(i).SetCellValue(averages[i - 1]); - } - - // 不再添加总平均值到时间列 - - // 应用样式 - for (int i = 0; i <= currentSampleCount; i++) - { - var cell = avgRow.GetCell(i); - if (cell != null) - { - cell.CellStyle = avgStyle; - } + sheet.AddMergedRegion(new CellRangeAddress(rowIndex, rowIndex, 1, currentSampleCount)); } } @@ -751,31 +773,45 @@ namespace WindowsFormsApp6 { int rowIndex = 3; - // 时间行 - IRow timeRow = sheet.CreateRow(rowIndex++); - timeRow.CreateCell(0).SetCellValue(TIME_ROW_LABEL); - timeRow.GetCell(0).CellStyle = dataStyle; + // 从数据表中获取时间行数据 + var timeRows = sampleDataTable.AsEnumerable() + .Where(r => r.Field("序号") == TIME_ROW_LABEL); - for (int i = 1; i <= currentSampleCount; i++) + if (timeRows.Any()) { - double value = 29 + i; // 示例数据 - ICell cell = timeRow.CreateCell(i); - cell.SetCellValue(value); - cell.CellStyle = yellowStyle; - } + foreach (var dataRow in timeRows) + { + IRow timeRow = sheet.CreateRow(rowIndex++); + ICell labelCell = timeRow.CreateCell(0); + labelCell.SetCellValue(TIME_ROW_LABEL); + labelCell.CellStyle = dataStyle; - // 不再添加"根据标准"列 + for (int i = 1; i <= currentSampleCount; i++) + { + ICell cell = timeRow.CreateCell(i); + cell.SetCellValue(dataRow.Field($"试样{i}")); + cell.CellStyle = yellowStyle; + } + } + } // 平均时间行 IRow avgRow = sheet.CreateRow(rowIndex); - avgRow.CreateCell(0).SetCellValue(AVG_ROW_LABEL); - avgRow.GetCell(0).CellStyle = dataStyle; + ICell avgLabelCell = avgRow.CreateCell(0); + avgLabelCell.SetCellValue(AVG_ROW_LABEL); + avgLabelCell.CellStyle = dataStyle; + // 计算并显示总平均值 + double overallAvg = CalculateOverallAverage(); ICell avgCell = avgRow.CreateCell(1); - avgCell.SetCellValue("32"); + avgCell.SetCellValue(overallAvg); avgCell.CellStyle = yellowStyle; - sheet.AddMergedRegion(new CellRangeAddress(rowIndex, rowIndex, 1, currentSampleCount)); + // 合并所有试样列 + if (currentSampleCount > 1) + { + sheet.AddMergedRegion(new CellRangeAddress(rowIndex, rowIndex, 1, currentSampleCount)); + } } /// diff --git a/WindowsFormsApp6/Form2.cs b/WindowsFormsApp6/Form2.cs index e8082e0..06c8aa4 100644 --- a/WindowsFormsApp6/Form2.cs +++ b/WindowsFormsApp6/Form2.cs @@ -29,8 +29,12 @@ namespace WindowsFormsApp6 private const string ROW_INITIAL_WEIGHT = "初始重量(g)"; private const string ROW_AFTER_WEIGHT = "浸润后重量(g)"; private const string ROW_ABSORPTION = "液体吸收量(%)"; + private const string ROW_SOAK_TIME = "浸润时间"; + private const string ROW_HANG_TIME = "悬挂时间"; + private const string ROW_RUN_SPEED = "运行速度"; private const string ROW_AVG_ABSORPTION = "液体吸收量平均值(%)"; private const string ROW_MAX_ABSORPTION = "液体吸收量最大值(%)"; + private const string ROW_STD_DEVIATION = "标准偏差"; #endregion #region 字段 @@ -297,8 +301,12 @@ namespace WindowsFormsApp6 { var rowsToRemove = sampleDataTable.AsEnumerable() .Where(r => r.Field("序号") == ROW_ABSORPTION || + r.Field("序号") == ROW_SOAK_TIME || + r.Field("序号") == ROW_HANG_TIME || + r.Field("序号") == ROW_RUN_SPEED || r.Field("序号") == ROW_AVG_ABSORPTION || - r.Field("序号") == ROW_MAX_ABSORPTION) + r.Field("序号") == ROW_MAX_ABSORPTION || + r.Field("序号") == ROW_STD_DEVIATION) .ToList(); foreach (var row in rowsToRemove) @@ -316,7 +324,7 @@ namespace WindowsFormsApp6 } /// - /// 生成模拟测试数据 + /// 生成模拟测试数据(精确模拟真实测试场景) /// public void GenerateMockData() { @@ -327,16 +335,45 @@ namespace WindowsFormsApp6 double[] initialWeights = new double[currentSampleCount]; double[] afterWeights = new double[currentSampleCount]; + // 设置基准值,使数据更真实 + double baseInitialWeight = 15.0; // 基准初始重量 + double baseAbsorptionRate = 0.35; // 基准吸收率 35% + for (int i = 0; i < currentSampleCount; i++) { - initialWeights[i] = Math.Round(MIN_WEIGHT + random.NextDouble() * (MAX_WEIGHT - MIN_WEIGHT), 2); - afterWeights[i] = Math.Round(initialWeights[i] * (1.2 + random.NextDouble() * 0.3), 2); // 浸润后增重20%-50% + // 初始重量:在基准值附近波动 ±3g + initialWeights[i] = Math.Round(baseInitialWeight + (random.NextDouble() - 0.5) * 6.0, 2); + + // 吸收率:在基准吸收率附近波动 ±10% + double absorptionRate = baseAbsorptionRate + (random.NextDouble() - 0.5) * 0.2; + + // 浸润后重量 = 初始重量 × (1 + 吸收率) + afterWeights[i] = Math.Round(initialWeights[i] * (1 + absorptionRate), 2); } sampleCount = 1; UpdateDisplay(initialWeights, afterWeights); - ShowMessage($"已生成 {currentSampleCount} 个试样的模拟数据", "模拟数据生成"); + // 计算并显示统计信息 + double[] absorptions = new double[currentSampleCount]; + for (int i = 0; i < currentSampleCount; i++) + { + absorptions[i] = ((afterWeights[i] - initialWeights[i]) / initialWeights[i]) * 100; + } + + double avgAbsorption = absorptions.Average(); + double stdDev = CalculateStandardDeviation(absorptions); + double maxAbsorption = absorptions.Max(); + double minAbsorption = absorptions.Min(); + + string message = $"已生成 {currentSampleCount} 个试样的模拟数据\n\n" + + $"液体吸收量统计:\n" + + $"平均值:{avgAbsorption:F2}%\n" + + $"标准偏差:{stdDev:F2}%\n" + + $"最大值:{maxAbsorption:F2}%\n" + + $"最小值:{minAbsorption:F2}%"; + + ShowMessage(message, "模拟数据生成"); } /// @@ -377,29 +414,57 @@ namespace WindowsFormsApp6 } sampleDataTable.Rows.Add(absorptionRow); - // 4. 液体吸收量平均值行 + // 4. 浸润时间行 + DataRow soakTimeRow = sampleDataTable.NewRow(); + soakTimeRow["序号"] = ROW_SOAK_TIME; + // 为每个试样生成浸润时间(基准60s,波动±10s) + for (int i = 1; i <= count; i++) + { + int soakTime = 60 + random.Next(-10, 11); // 50-70秒 + soakTimeRow[$"试样{i}"] = $"{soakTime}s"; + } + sampleDataTable.Rows.Add(soakTimeRow); + + // 5. 悬挂时间行 + DataRow hangTimeRow = sampleDataTable.NewRow(); + hangTimeRow["序号"] = ROW_HANG_TIME; + // 为每个试样生成悬挂时间(基准30s,波动±5s) + for (int i = 1; i <= count; i++) + { + int hangTime = 30 + random.Next(-5, 6); // 25-35秒 + hangTimeRow[$"试样{i}"] = $"{hangTime}s"; + } + sampleDataTable.Rows.Add(hangTimeRow); + + // 6. 运行速度行 + DataRow runSpeedRow = sampleDataTable.NewRow(); + runSpeedRow["序号"] = ROW_RUN_SPEED; + // 为每个试样生成运行速度(基准100mm/min,波动±10mm/min) + for (int i = 1; i <= count; i++) + { + int runSpeed = 100 + random.Next(-10, 11); // 90-110 mm/min + runSpeedRow[$"试样{i}"] = $"{runSpeed}mm/min"; + } + sampleDataTable.Rows.Add(runSpeedRow); + + // 7. 液体吸收量平均值行 DataRow avgRow = sampleDataTable.NewRow(); avgRow["序号"] = ROW_AVG_ABSORPTION; - double avgAbsorption = absorptions.Average(); - double stdDev = CalculateStandardDeviation(absorptions); + + // 根据实际试样数量计算平均值 + double avgAbsorption = count > 0 ? absorptions.Take(count).Average() : 0; // 第1列显示平均值 avgRow["试样1"] = avgAbsorption; - // 第2列显示标准偏差 - if (count >= 2) - { - avgRow["试样2"] = stdDev; - } - // 其他列为空 - for (int i = 3; i <= count; i++) + for (int i = 2; i <= count; i++) { avgRow[$"试样{i}"] = DBNull.Value; } sampleDataTable.Rows.Add(avgRow); - // 5. 液体吸收量最大值行 + // 8. 液体吸收量最大值行 DataRow maxRow = sampleDataTable.NewRow(); maxRow["序号"] = ROW_MAX_ABSORPTION; double maxAbsorption = absorptions.Max(); @@ -414,20 +479,38 @@ namespace WindowsFormsApp6 } sampleDataTable.Rows.Add(maxRow); + // 9. 标准偏差行(最后一行) + DataRow stdDevRow = sampleDataTable.NewRow(); + stdDevRow["序号"] = ROW_STD_DEVIATION; + + // 根据实际试样数量计算标准偏差 + double stdDev = count > 1 ? CalculateStandardDeviation(absorptions.Take(count).ToArray()) : 0; + + // 第1列显示标准偏差 + stdDevRow["试样1"] = stdDev; + + // 其他列为空 + for (int i = 2; i <= count; i++) + { + stdDevRow[$"试样{i}"] = DBNull.Value; + } + sampleDataTable.Rows.Add(stdDevRow); + // 刷新界面 RefreshDataGridView(); } /// - /// 计算标准偏差 + /// 计算标准偏差(样本标准偏差,n-1) /// private double CalculateStandardDeviation(double[] values) { - if (values.Length == 0) return 0; + if (values == null || values.Length <= 1) return 0; double avg = values.Average(); double sumOfSquares = values.Sum(val => Math.Pow(val - avg, 2)); - return Math.Sqrt(sumOfSquares / values.Length); + // 使用样本标准偏差公式:除以 (n-1) + return Math.Sqrt(sumOfSquares / (values.Length - 1)); } /// diff --git a/WindowsFormsApp6/Form3.cs b/WindowsFormsApp6/Form3.cs index 9a23828..c39c095 100644 --- a/WindowsFormsApp6/Form3.cs +++ b/WindowsFormsApp6/Form3.cs @@ -862,75 +862,74 @@ namespace WindowsFormsApp6 /// /// 计算平均芯吸速率(mm/min) + /// 计算方式:每组试样显示该组3次测试的平均值 /// private void CalculateAverageWickingRate() { - List rates = new List(); - if (isVerticalLayout) { // 纵向布局 DataRow rateRow = sampleDataTable.Rows[2]; DataRow avgRow = sampleDataTable.Rows[3]; + // 计算每组试样的平均值,并填充到对应列 for (int i = 1; i <= currentSampleCount; i++) { double rate1 = ConvertToDouble(rateRow[$"试样{i}_1"]); double rate2 = ConvertToDouble(rateRow[$"试样{i}_2"]); double rate3 = ConvertToDouble(rateRow[$"试样{i}_3"]); - if (rate1 > 0) rates.Add(rate1); - if (rate2 > 0) rates.Add(rate2); - if (rate3 > 0) rates.Add(rate3); - } - - double average = rates.Count > 0 ? rates.Average() : 0; - - for (int i = 1; i <= currentSampleCount; i++) - { - avgRow[$"试样{i}_3"] = Math.Round(average, 2); + List groupRates = new List(); + if (rate1 > 0) groupRates.Add(rate1); + if (rate2 > 0) groupRates.Add(rate2); + if (rate3 > 0) groupRates.Add(rate3); + + // 计算该组平均值 + double groupAvg = groupRates.Count > 0 ? groupRates.Average() : 0; + + // 填充到该试样的第3列 + avgRow[$"试样{i}_3"] = Math.Round(groupAvg, 2); } } else { // 横向布局 - foreach (DataRow row in sampleDataTable.Rows) - { - double rate = ConvertToDouble(row[ROW_WICKING_RATE]); - if (rate > 0) rates.Add(rate); - } - - double average = rates.Count > 0 ? rates.Average() : 0; - - // 只在每个试样的第3次测试显示平均值 - int rowIndex = 0; + // 计算每组试样的平均值 for (int i = 1; i <= currentSampleCount; i++) { + List groupRates = new List(); + for (int j = 1; j <= 3; j++) { + int rowIndex = (i - 1) * 3 + (j - 1); if (rowIndex < sampleDataTable.Rows.Count) { - if (j == 3) - { - sampleDataTable.Rows[rowIndex][ROW_AVG_WICKING_RATE] = Math.Round(average, 2); - } - else - { - sampleDataTable.Rows[rowIndex][ROW_AVG_WICKING_RATE] = 0.0; - } - rowIndex++; + double rate = ConvertToDouble(sampleDataTable.Rows[rowIndex][ROW_WICKING_RATE]); + if (rate > 0) groupRates.Add(rate); } } + + // 计算该组平均值 + double groupAvg = groupRates.Count > 0 ? groupRates.Average() : 0; + + // 填充到该组的第3次测试行 + int targetRowIndex = (i - 1) * 3 + 2; // 第3次测试的行索引 + if (targetRowIndex < sampleDataTable.Rows.Count) + { + sampleDataTable.Rows[targetRowIndex][ROW_AVG_WICKING_RATE] = Math.Round(groupAvg, 2); + } } } } /// /// 计算标准偏差 + /// 计算方式:基于每组试样的平均芯吸速率计算标准偏差(组间标准偏差) + /// 结果:只有一个标准偏差值,合并显示在第1列 /// private void CalculateStandardDeviation() { - List rates = new List(); + List groupAverages = new List(); // 存储每组的平均值 if (isVerticalLayout) { @@ -938,64 +937,102 @@ namespace WindowsFormsApp6 DataRow rateRow = sampleDataTable.Rows[2]; DataRow stdRow = sampleDataTable.Rows[4]; + // 计算每组试样的平均值 for (int i = 1; i <= currentSampleCount; i++) { double rate1 = ConvertToDouble(rateRow[$"试样{i}_1"]); double rate2 = ConvertToDouble(rateRow[$"试样{i}_2"]); double rate3 = ConvertToDouble(rateRow[$"试样{i}_3"]); - if (rate1 > 0) rates.Add(rate1); - if (rate2 > 0) rates.Add(rate2); - if (rate3 > 0) rates.Add(rate3); + List groupRates = new List(); + if (rate1 > 0) groupRates.Add(rate1); + if (rate2 > 0) groupRates.Add(rate2); + if (rate3 > 0) groupRates.Add(rate3); + + // 如果该组有有效数据,计算该组平均值 + if (groupRates.Count > 0) + { + double groupAvg = groupRates.Average(); + groupAverages.Add(groupAvg); + } } + // 基于组平均值计算标准偏差 double stdDev = 0; - if (rates.Count > 1) + if (groupAverages.Count > 1) { - double average = rates.Average(); - double sumOfSquares = rates.Sum(r => Math.Pow(r - average, 2)); - stdDev = Math.Sqrt(sumOfSquares / (rates.Count - 1)); + double overallAverage = groupAverages.Average(); + double sumOfSquares = groupAverages.Sum(avg => Math.Pow(avg - overallAverage, 2)); + stdDev = Math.Sqrt(sumOfSquares / (groupAverages.Count - 1)); } + // 只在第1列显示标准偏差,其他列清空 for (int i = 1; i <= currentSampleCount; i++) { - stdRow[$"试样{i}_3"] = Math.Round(stdDev, 2); + if (i == 1) + { + stdRow[$"试样{i}_1"] = Math.Round(stdDev, 2); + } + else + { + stdRow[$"试样{i}_1"] = 0.0; + stdRow[$"试样{i}_2"] = 0.0; + stdRow[$"试样{i}_3"] = 0.0; + } } } else { // 横向布局 - foreach (DataRow row in sampleDataTable.Rows) + // 计算每组试样的平均值 + for (int i = 1; i <= currentSampleCount; i++) { - double rate = ConvertToDouble(row[ROW_WICKING_RATE]); - if (rate > 0) rates.Add(rate); + List groupRates = new List(); + + for (int j = 1; j <= 3; j++) + { + int rowIndex = (i - 1) * 3 + (j - 1); + if (rowIndex < sampleDataTable.Rows.Count) + { + double rate = ConvertToDouble(sampleDataTable.Rows[rowIndex][ROW_WICKING_RATE]); + if (rate > 0) groupRates.Add(rate); + } + } + + // 如果该组有有效数据,计算该组平均值 + if (groupRates.Count > 0) + { + double groupAvg = groupRates.Average(); + groupAverages.Add(groupAvg); + } } + // 基于组平均值计算标准偏差 double stdDev = 0; - if (rates.Count > 1) + if (groupAverages.Count > 1) { - double average = rates.Average(); - double sumOfSquares = rates.Sum(r => Math.Pow(r - average, 2)); - stdDev = Math.Sqrt(sumOfSquares / (rates.Count - 1)); + double overallAverage = groupAverages.Average(); + double sumOfSquares = groupAverages.Sum(avg => Math.Pow(avg - overallAverage, 2)); + stdDev = Math.Sqrt(sumOfSquares / (groupAverages.Count - 1)); } - // 只在每个试样的第3次测试显示标准偏差 - int rowIndex = 0; + // 只在第1行显示标准偏差,其他行清空 + int dataRowIndex = 0; for (int i = 1; i <= currentSampleCount; i++) { for (int j = 1; j <= 3; j++) { - if (rowIndex < sampleDataTable.Rows.Count) + if (dataRowIndex < sampleDataTable.Rows.Count) { - if (j == 3) + if (dataRowIndex == 0) // 只在第1行显示 { - sampleDataTable.Rows[rowIndex][ROW_STD_DEVIATION] = Math.Round(stdDev, 2); + sampleDataTable.Rows[dataRowIndex][ROW_STD_DEVIATION] = Math.Round(stdDev, 2); } else { - sampleDataTable.Rows[rowIndex][ROW_STD_DEVIATION] = 0.0; + sampleDataTable.Rows[dataRowIndex][ROW_STD_DEVIATION] = 0.0; } - rowIndex++; + dataRowIndex++; } } }