使用匹配和插值在数据表中查找查找值
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.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,程序将 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);
}
一如既往,如果有任何问题,请联系这里。 :)
我有一个从 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.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,程序将 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);
}
一如既往,如果有任何问题,请联系这里。 :)