【问题标题】:Finding lookup values in datatable with match and with interpolation使用匹配和插值在数据表中查找查找值
【发布时间】:2021-08-21 01:56:49
【问题描述】:

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

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

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

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

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

如果没有匹配,那么我需要找到 LookupValue 所在位置两侧的行,并返回 L、M、S 中每个变量的最小/最大值。

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

例如:

Col_0 L M S
45.0 -0.3521 2.441 0.09182
45.5 -0.3521 2.524 0.09153
46.0 -0.3521 2.608 0.09124
46.5 -0.3521 2.691 0.09094
47.0 -0.3521 2.776 0.09065
47.5 -0.3521 2.861 0.09036
48.0 -0.3521 2.948 0.09007

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

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

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

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

Col_0 L Values M Values S Values
LookupMin = 46.5 Lmin = -0.3521 Mmin = 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 值将被放入用户的文本框中。

我有一些代码可以在匹配时工作,但是,我认为使用 Linq 或 Tuples 有更好/更简洁的方法。我意识到这不是最好的代码,我愿意接受建议。

我搜索了 StackOverflow 并找到了几篇关于插值和查找表的帖子。查找的最佳实践似乎是使用元组,但是,我对它们的使用不是很清楚。

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

感谢任何帮助。

        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");
            }

【问题讨论】:

    标签: c# tuples interpolation lookup-tables


    【解决方案1】:

    您询问是否可能使用 LINQ,所以我检查了我的代码箱,发现了类似的东西,我适应了您的需求。

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

    扩展方法返回三个输出参数,其中包含找到的索引,如果没有找到,则返回 -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);
    }
    

    与往常一样,如果有任何问题,这里就是地方。 :)

    【讨论】:

    • 感谢您的努力。我尝试使用您的代码创建控制台。创建“foundAnything”时出现错误。我已经更改/添加/编辑了很多东西都无济于事。你介意发布你的整个控制台代码吗?我得到的错误是“IEnumerable”不包含“GetNearestOrEqual”的定义,并且找不到可访问的扩展方法。我为 Linq 添加了程序集并更改了一些详细的方法调用。我还必须从 GetNearestOrEqual 中取出 'static' 和 'this' 来解决错误。
    • 那里,所以我有点懒惰,并添加了一些关于如何将代码复制到干净的控制台应用程序中的信息。
    • 谢谢!我没想到插值,但是,这很完美。
    • 不用担心。很高兴为您服务。 ;)
    猜你喜欢
    • 2018-05-08
    • 2017-12-24
    • 2015-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多