如何在 Excel 中使用 C# 有效地比较连续行和顺序行?
How can I efficiently compare contiguous and sequential rows using C# in Excel?
我正在用 C# 为 Excel 开发一个 VSTO 插件,它需要比较潜在的大数据集(100 列 x ~10000 或更多行)。它正在 Excel 中完成,因此最终用户可以逐行查看所提供数据的一些图形表示。尽管使用这些大型数据集存在潜在缺陷,但此应用程序必须在 Excel 内完成。
无论如何,我的问题与比较连续行和连续行的有效方法有关。我的目标是将一行与紧随其后的一行进行比较;如果 row1 和 row2 之间的任何元素发生变化,这将计为 "event" 并且 row2 输出到单独的 sheet。我相信您可以看到,当计数约为 10000 时,对于行的逐行比较,这需要很长时间(实际上,对于当前代码,每行大约需要 150 毫秒到 200 毫秒)。
目前我使用SequenceEqual()
方法比较两个字符串列表如下:
private void FilterRawDataForEventReader(Excel.Application xlApp)
{
List<string> row1 = new List<string>();
List<string> row2 = new List<string>();
xlWsRaw = xlApp.Worksheets["Full Raw Data"];
xlWsEventRaw = xlApp.Worksheets["Event Data"];
Excel.Range xlRawRange = xlWsRaw.Range["A3"].Resize[xlWsRaw.UsedRange.Rows.Count-2, xlWsRaw.UsedRange.Columns.Count];
var array = xlRawRange.Value;
Excel.Range xlRange = (Excel.Range)xlWsEventRaw.Cells[xlWsEventRaw.UsedRange.Rows.Count, 1];
int lastRow = xlRange.get_End(Excel.XlDirection.xlUp).Row;
int newRow = lastRow + 2;
for (int i = 1; i < xlWsRaw.UsedRange.Rows.Count - 2; i++)
{
row1.Clear();
row2.Clear();
for (int j = 1; j <= xlWsRaw.UsedRange.Columns.Count-1; j++)
{
row1.Add(array[i, j].ToString());
row2.Add(array[i + 1, j].ToString());
}
if (!row1.SequenceEqual(row2))
{
row2.Add(array[i + 1, xlWsRaw.UsedRange.Columns.Count].ToString()); // Add timestamp to row2.
for (int j = 0; j < row2.Count; j++)
{
xlWsEventRaw.Cells[newRow, j + 1] = row2[j];
}
newRow++;
}
}
}
在测试期间,我在该方法的各个部分放置了计时器,以查看某些操作需要多长时间。对于 100 列,为 row1 和 row2 构建字符串数组的第一个循环每次迭代大约需要 100 毫秒,当找到 "event" 时,整个操作需要 150 毫秒到 200 毫秒。
我的直觉是构建两个 List<string>
是问题所在,但根据我的经验,我不知道如何解决此类问题。我要强调的是,两个 List<string>
中数据的实际值无关紧要;重要的是数据是否完全不同。那样的话,我觉得我正在错误地处理这个问题,但不知道如何"re-approach"这么说。
我想知道,如果不是通过迭代构建字符串数组并将它们与 SequenceEqual()
方法进行比较,是否有人可以建议一种更快的方法来比较连续行和顺序行?
如果此解决方案可能对尝试在 C# 中使用 Excel 并进行一些比较的其他人有用:
这个问题主要是一个优化练习。通过消除多个循环并使用 Excel 代替生成比较列表:
for (int i = 3; i < xlWsRaw.UsedRange.Rows.Count - 2; i++)
{
rng1 = (Excel.Range)xlWsRaw.Range[xlWsRaw.Cells[i, 1], xlWsRaw.Cells[i, xlWsRaw.UsedRange.Columns.Count - 1]];
rng2 = (Excel.Range)xlWsRaw.Range[xlWsRaw.Cells[i+1, 1], xlWsRaw.Cells[i+1, xlWsRaw.UsedRange.Columns.Count - 1]];
rng3 = (Excel.Range)xlWsEventRaw.Range[xlWsEventRaw.Cells[newRow, 1], xlWsEventRaw.Cells[newRow, xlWsRaw.UsedRange.Columns.Count - 1]];
object[,] cellValues1 = (object[,])rng1.Value2;
object[,] cellValues2 = (object[,])rng2.Value2;
List<string> test1 = cellValues1.Cast<object>().ToList().ConvertAll(x => Convert.ToString(x));
List<string> test2 = cellValues2.Cast<object>().ToList().ConvertAll(x => Convert.ToString(x));
if (!test1.SequenceEqual(test2))
{
rng2.Copy(rng3);
xlWsEventRaw.Cells[newRow, xlWsRaw.UsedRange.Columns.Count].Value = xlWsRaw.Cells[i + 1, xlWsRaw.UsedRange.Columns.Count].Value; // Outputs the timestamp of the event to the events worksheet.
newRow++;
}
}
我相信这可以进一步优化,但在我的例子中,范围包含多种类型,包括字符串,所以为了比较,我将所有内容都转换为 List<string>
。 SequenceEqual()
方法,无论它在幕后如何工作,几乎是瞬时的,并将比较 120 列的时间减少到大约 3 毫秒。
我正在用 C# 为 Excel 开发一个 VSTO 插件,它需要比较潜在的大数据集(100 列 x ~10000 或更多行)。它正在 Excel 中完成,因此最终用户可以逐行查看所提供数据的一些图形表示。尽管使用这些大型数据集存在潜在缺陷,但此应用程序必须在 Excel 内完成。
无论如何,我的问题与比较连续行和连续行的有效方法有关。我的目标是将一行与紧随其后的一行进行比较;如果 row1 和 row2 之间的任何元素发生变化,这将计为 "event" 并且 row2 输出到单独的 sheet。我相信您可以看到,当计数约为 10000 时,对于行的逐行比较,这需要很长时间(实际上,对于当前代码,每行大约需要 150 毫秒到 200 毫秒)。
目前我使用SequenceEqual()
方法比较两个字符串列表如下:
private void FilterRawDataForEventReader(Excel.Application xlApp)
{
List<string> row1 = new List<string>();
List<string> row2 = new List<string>();
xlWsRaw = xlApp.Worksheets["Full Raw Data"];
xlWsEventRaw = xlApp.Worksheets["Event Data"];
Excel.Range xlRawRange = xlWsRaw.Range["A3"].Resize[xlWsRaw.UsedRange.Rows.Count-2, xlWsRaw.UsedRange.Columns.Count];
var array = xlRawRange.Value;
Excel.Range xlRange = (Excel.Range)xlWsEventRaw.Cells[xlWsEventRaw.UsedRange.Rows.Count, 1];
int lastRow = xlRange.get_End(Excel.XlDirection.xlUp).Row;
int newRow = lastRow + 2;
for (int i = 1; i < xlWsRaw.UsedRange.Rows.Count - 2; i++)
{
row1.Clear();
row2.Clear();
for (int j = 1; j <= xlWsRaw.UsedRange.Columns.Count-1; j++)
{
row1.Add(array[i, j].ToString());
row2.Add(array[i + 1, j].ToString());
}
if (!row1.SequenceEqual(row2))
{
row2.Add(array[i + 1, xlWsRaw.UsedRange.Columns.Count].ToString()); // Add timestamp to row2.
for (int j = 0; j < row2.Count; j++)
{
xlWsEventRaw.Cells[newRow, j + 1] = row2[j];
}
newRow++;
}
}
}
在测试期间,我在该方法的各个部分放置了计时器,以查看某些操作需要多长时间。对于 100 列,为 row1 和 row2 构建字符串数组的第一个循环每次迭代大约需要 100 毫秒,当找到 "event" 时,整个操作需要 150 毫秒到 200 毫秒。
我的直觉是构建两个 List<string>
是问题所在,但根据我的经验,我不知道如何解决此类问题。我要强调的是,两个 List<string>
中数据的实际值无关紧要;重要的是数据是否完全不同。那样的话,我觉得我正在错误地处理这个问题,但不知道如何"re-approach"这么说。
我想知道,如果不是通过迭代构建字符串数组并将它们与 SequenceEqual()
方法进行比较,是否有人可以建议一种更快的方法来比较连续行和顺序行?
如果此解决方案可能对尝试在 C# 中使用 Excel 并进行一些比较的其他人有用:
这个问题主要是一个优化练习。通过消除多个循环并使用 Excel 代替生成比较列表:
for (int i = 3; i < xlWsRaw.UsedRange.Rows.Count - 2; i++)
{
rng1 = (Excel.Range)xlWsRaw.Range[xlWsRaw.Cells[i, 1], xlWsRaw.Cells[i, xlWsRaw.UsedRange.Columns.Count - 1]];
rng2 = (Excel.Range)xlWsRaw.Range[xlWsRaw.Cells[i+1, 1], xlWsRaw.Cells[i+1, xlWsRaw.UsedRange.Columns.Count - 1]];
rng3 = (Excel.Range)xlWsEventRaw.Range[xlWsEventRaw.Cells[newRow, 1], xlWsEventRaw.Cells[newRow, xlWsRaw.UsedRange.Columns.Count - 1]];
object[,] cellValues1 = (object[,])rng1.Value2;
object[,] cellValues2 = (object[,])rng2.Value2;
List<string> test1 = cellValues1.Cast<object>().ToList().ConvertAll(x => Convert.ToString(x));
List<string> test2 = cellValues2.Cast<object>().ToList().ConvertAll(x => Convert.ToString(x));
if (!test1.SequenceEqual(test2))
{
rng2.Copy(rng3);
xlWsEventRaw.Cells[newRow, xlWsRaw.UsedRange.Columns.Count].Value = xlWsRaw.Cells[i + 1, xlWsRaw.UsedRange.Columns.Count].Value; // Outputs the timestamp of the event to the events worksheet.
newRow++;
}
}
我相信这可以进一步优化,但在我的例子中,范围包含多种类型,包括字符串,所以为了比较,我将所有内容都转换为 List<string>
。 SequenceEqual()
方法,无论它在幕后如何工作,几乎是瞬时的,并将比较 120 列的时间减少到大约 3 毫秒。