使用匹配和插值在数据表中查找查找值

Finding lookup values in datatable with match and with interpolation

我有一个从 CSV 文件导入的数据表。可以导入大量不同的表。

数据表有四列(type=double):LookupColumn L M S

LookupColumn 通常有一个唯一的名称(例如,长度、高度、重量)。其他列名称保持不变。这无关紧要,因为您可以主要使用 dt.Column[0]。查找列将始终是导入的第一列。

我需要在 LookupColumn 上的数据表中搜索从应用程序(从文本框)传递的 LookupValue。

如果 LookupValue 与 LookupColumn 中的数字完全匹配,则 return L、M、S 的值。

如果没有匹配项,那么我需要找到 LookupValue 所在位置两侧的行和 return L、M、S 中每个变量的 min/max 值。

一旦我有了这些,我就可以插入 L、M、S 的值。

例如:

Col_0 L S
45.0 -0.3521 2.441 0.09​​182
45.5 -0.3521 2.524 0.09​​153
46.0 -0.3521 2.608 0.09​​124
46.5 -0.3521 2.691 0.09​​094
47.0 -0.3521 2.776 0.09​​065
47.5 -0.3521 2.861 0.09​​036
48.0 -0.3521 2.948 0.09​​007

如果我在 Col[0] 中的 LookupValue = 46.5,程序将 return L=-0.3521 M=2.691 S=0.09094

这些值将放在查看者看到的表单的文本框中。

如果没有匹配项(假设 LookupValue 在 LookupColumn min/max 范围内),那么我需要 return 如果它存在,值所在位置两侧的行--即 Lmin Lmax、Mmin Mmax、Smin Smax 并使用以下公式中的那些来获取 LookupColumn (Col_0) 的插值 (IntVal)。

例如,如果 (Col_0) 中的 LookupValue = 46.8,则 returned 结果(数组?列表?)将是 Col_0 = 46.5 和47.0:

Col_0 L 值 M 值 S 值
LookupMin = 46.5 Lmin = -0.3521 最小值 = 2.691 Smin = 0.09094
LookupMax = 47.0 Lmax = -0.3521 Mmax = 2.776 Smax = 0.09065

内插值 = LMSmin + (46.8 - LookupMin) * (LMSmax - LMSmin / LookupMax - LookupMin)

内插 L = -0.3521 因为 Lmin = Lmax

内插 M = 2.691 + (46.8 - 46.5) * (2.776 - 2.691 / 47.0 - 46.5)
内插 M = 2.7418

内插 S = 0.09094 + (46.8 - 46.5) * (0.09065 - 0.09094 / 47.0 - 46.5)
内插 S = 0.09088

因此,给定 Col_0 的 Min/Max 值以及 L、M 或 S min/max 值,我可以插入用户提供的任何不在查找,即使 LookupValue 有更多小数。内插的 L、M、S 值将放入用户的文本框中。

我有一些代码可以在匹配时运行,但是,我认为有一种 better/more 简洁的方法可以使用 Linq 或元组。我意识到这不是最好的代码,我愿意接受建议。

我搜索了 Whosebug,发现了一些关于插值和查找表的帖子。查找的最佳实践似乎是使用元组,但是,我不太清楚它们的用途。

在大多数情况下,这个问题的重点是 return 在没有匹配项的情况下查找 Min/Max 值。一旦我有了这些,我不认为插值是一个很大的壮举,因为我知道这个公式。另外,我知道用户可能会输入超出范围的值——我稍后会解决这些问题。

感谢任何帮助。

        private void tbLookup_Leave(object sender, System.EventArgs e)
        {
            string colName = tmpDT.Columns[0].ColumnName;
            string colSearch = colName + " = '" + tbLookup.Text + "'";


            if (tbLookup.Text.Length > 0)
            {
               // Exact match                
               while (true)
                {
                    DataRow[] foundRow = tmpDT.Select(colSearch);
                    if (foundRow.Length == 0)
                    {
                        break;
                    }
                    foreach (DataRow row in foundRow)
                    {
                        string L = row.Field<string>("L");
                        string M = row.Field<string>("M");
                        string S = row.Field<string>("S");

                        tbLkupL.Text = L;
                        tbLkupM.Text = M;
                        tbLkupS.Text = S;

                    }
                    // No match
                    // Call interpolation method
                }
            }
            else
            {
                MessageBox.Show("Please enter a lookup value", "Missing Data");
            }

您询问是否可能使用 LINQ,所以我检查了我的代码箱并找到了类似的东西,我根据您的需要进行了调整。

using System.Linq; // Add this at the top of the Program.cs file.

扩展方法returns 三个输出参数,包含已找到的索引,如果未找到则为 -1。

// An extension methods class must be the first class in a file.
// Add this class inside the namespace of a console app, before the Program class (in the Program.cs file).
public static class ExtensionMethods
{
  public static bool GetNearestOrEqual<TSource, TValue>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, TValue> valueSelector, TValue referenceValue, out int indexOfLowerMax, out int indexOfEqual, out int indexOfHigherMin)
  where TValue : struct, System.IComparable<TValue>
  {
    using var e = source.GetEnumerator();

    var ltCurrent = new TValue?();
    var gtCurrent = new TValue?();

    indexOfLowerMax = -1;
    indexOfEqual = -1;
    indexOfHigherMin = -1;

    var index = 0;

    while (e.MoveNext())
    {
      var currentValue = valueSelector(e.Current);

      switch (currentValue.CompareTo(referenceValue))
      {
        case int lo when lo < 0:
          if (!ltCurrent.HasValue || currentValue.CompareTo(ltCurrent.Value) > 0)
          {
            indexOfLowerMax = index;
            ltCurrent = currentValue;
          }
          break;
        case int hi when hi > 0:
          if (!gtCurrent.HasValue || currentValue.CompareTo(gtCurrent.Value) < 0)
          {
            indexOfHigherMin = index;
            gtCurrent = currentValue;
          }
          break;
        default:
          indexOfEqual = index;
          break;
      }

      index++;
    }

    return indexOfLowerMax != -1 || indexOfEqual != -1 || indexOfHigherMin != -1;
  }
}

如何使用它的示例(创建了一个简单的控制台应用程序):

// Replace the Main() inside the Program class of a console app.
static void Main(string[] args)
{
  var dt = new System.Data.DataTable();

  dt.Columns.Add("Col_0", typeof(double));
  dt.Columns.Add("L", typeof(double));
  dt.Columns.Add("M", typeof(double));
  dt.Columns.Add("S", typeof(double));

  dt.Rows.Add(new object[] { 45.0, -0.3521, 2.441, 0.09182 });
  dt.Rows.Add(new object[] { 45.5, -0.3521, 2.524, 0.09153 });
  dt.Rows.Add(new object[] { 46.0, -0.3521, 2.608, 0.09124 });
  dt.Rows.Add(new object[] { 46.5, -0.3521, 2.691, 0.09094 });
  dt.Rows.Add(new object[] { 47.0, -0.3521, 2.776, 0.09065 });
  dt.Rows.Add(new object[] { 47.5, -0.3521, 2.861, 0.09036 });
  dt.Rows.Add(new object[] { 48.0, -0.3521, 2.948, 0.09007 });

  var lookupValue = 46.8;

  var foundAnything = dt.Rows.Cast<System.Data.DataRow>().GetNearestOrEqual(o => (double)o.ItemArray[0], lookupValue, out var indexOfLowerMax, out var indexOfEqual, out var indexOfHigherMin);

  // Assuming example for when both low and high are found...

  var dr = dt.NewRow();

  var lookuploDiff = lookupValue - (double)dt.Rows[indexOfLowerMax][0];
  var hiloDiff = (double)dt.Rows[indexOfHigherMin][0] - (double)dt.Rows[indexOfLowerMax][0];

  dr.ItemArray = new object[] {
    lookupValue,
    (double)dt.Rows[indexOfLowerMax][1] + lookuploDiff * (((double)dt.Rows[indexOfHigherMin][1] - (double)dt.Rows[indexOfLowerMax][1]) / hiloDiff),
    (double)dt.Rows[indexOfLowerMax][2] + lookuploDiff * (((double)dt.Rows[indexOfHigherMin][2] - (double)dt.Rows[indexOfLowerMax][2]) / hiloDiff),
    (double)dt.Rows[indexOfLowerMax][3] + lookuploDiff * (((double)dt.Rows[indexOfHigherMin][3] - (double)dt.Rows[indexOfLowerMax][3]) / hiloDiff),
  };

  dt.Rows.InsertAt(dr, indexOfHigherMin);
}

一如既往,如果有任何问题,请联系这里。 :)