2014-10-07 45 views
0

我目前对PowerShell和编程通常比较陌生。我正在开发PowerShell中的一个工具,它使用两个CSV文件,每行包含5,000-40,000行和30+标头。该工具将获取这两个文件,并根据关键字(符号)查找匹配行并报告相应字段中的差异。Powershell:如何减少两个阵列的比较时间

的脚本需要两个参数:要比较两个CSV文件中。以下代表作为gcFile1和gcFile2

这些CSV文件将不具有行或全部相同符号的相同的量,但是按字母顺序排列。

我能够匹配基于我所希望的关键字符串,执行比较和正确输出的差异。

我的问题是,它需要完全太长,我的假设是因为我使用两个foreach循环到它基本上这个比较对象的每一行,使得它需要比允许更长的时间。

我找了该线路的方式从后续搜索一旦被用来制造更小的阵列,通过每次搜索中移除。

非常感谢你的帮助:)

这里是包含foreach循环的代码片段:

#For each line in the first file 
ForEach($line1 in $gcFile1) 
{ 

    #For Each line in the second file 
    ForEach($line2 in $gcFile2) 
    { 

    #If the symbol from file one is like a symbol from file 2 
    If($line1.Split(';').Get(0) -like $line2.Split(';').Get(0)) 
    { 

     $Symbol1 = $line1.Split(';').Get(0) 
     $Symbol2 = $line2.Split(';').Get(0) 

     for($x=0;$x -lt $headerCount1; $x++) 
     { 
      If($line1.Split(';').Get($x) -like $line2.Split(';').Get($x)) 
      { 
       $Version1 = $line1.Split(';').Get($x) 
       $Version2 = $line2.Split(';').Get($x) 

      } else { 

       $Version1 = $line1.Split(';').Get($x) 
       $Version2 = $line2.Split(';').Get($x) 
       $headerName1 = $headerArray1[$x] 
       $headerName2 = $headerArray2[$x] 

       $bufferLength = 30 - $headerName1.Length 
       $pad = " " 


       for($y = 0;$y -lt $bufferLength; $y++){ 

       $pad += " " 

       } 

       Write-Host "[$headerName1]$pad[$Version1/$Version2]" 
       Add-Content $logfileBoth "[$headerName1]$pad[$Version1/$Version2]" 


      } 
     }                          
    } 
} 
} 

从CSV示例:

Symbol;Validity;AnnualHighDate-Date;AnnualHighDate-Time;AnnualLowDate-Date;AnnualLowDate- Time;AverageVolume100Day;AverageVolume22Day;Beta;ClosePriceMonth;ClosePriceQuarter;ClosePriceWeek;Clo sePriceYear;HighPriceCalendar;LowPriceCalendar;Mo12RateOfReturn;MovingAverage100Day;MovingAverage14Day;MovingAverage200Day;MovingAverage21Day;MovingAverage50Day;MovingAverage9Day;Volatility20Day;Volatility6Month;YTDRateOfReturn;AverageVolume250;HighDateCalendar;Size;AnnualHighDate;AnnualLowDate;CalcLastUpdate 
A;valid;20140122;0;20130904;0;1.81273e+006;1.85068e+006;1.3787;57.16;57.44;57.16;57.19;61.22;51.96;0.2481;56.54;57.68;56.59;56.81;56.92;57.67;0.1804;0.1796;0.0198;2320468;20140122;248;1/22/2014;9/4/2013;9/3/2014 
AA;valid;20140723;0;20130904;0;1.52891e+007;1.1017e+007;1.5202;16.61;14.89;16.61;10.63;17.22;9.82;1.2085;14.92;16.49;13.02;16.4;16.11;16.59;0.146;0.2494;0.6011;22428276;20140723;248;7/23/2014;9/4/2013;9/3/2014 

例如我将在文件1中找到符号A,在符号A中搜索文件2并比较对应于相同头的列。

期望的结果是用符号和的哪些列是不同的列表的输出,具有版本1和版本2

样本输出:

============================== A ============================== 

[Header] [file1.txt/file2.txt] 

[AverageVolume100Day]   [1.84354e+006/1.81273e+006] 
[AverageVolume22Day]    [1.85629e+006/1.85068e+006] 
[Beta]       [1.5311/1.3787] 
[Mo12RateOfReturn]    [0.2484/0.2481] 
[MovingAverage100Day]   [56.4635/56.54] 
[MovingAverage14Day]    [57.455/57.68] 
[MovingAverage200Day]   [56.5412/56.59] 
[MovingAverage21Day]    [56.7281/56.81] 
[MovingAverage50Day]    [56.9214/56.92] 
[MovingAverage9Day]    [57.7011/57.67] 
[Volatility20Day]    [0.0508/0.1804] 
[Volatility6Month]    [0.1285/0.1796] 
[YTDRateOfReturn]    [0.02/0.0198] 
[AverageVolume250]    [2325140/2320468] 

============================== AA ============================== 

[Header] [file1.txt/file2.txt] 

[AverageVolume100Day]   [1.58983e+007/1.52891e+007] 
[AverageVolume22Day]    [1.11858e+007/1.1017e+007] 
[Beta]       [1.6706/1.5202] 
[LowPriceCalendar]    [9.825/9.82] 
[Mo12RateOfReturn]    [1.1749/1.2085] 
[MovingAverage100Day]   [14.8568/14.92] 
[MovingAverage14Day]    [16.4471/16.49] 
[MovingAverage200Day]   [12.9426/13.02] 
[MovingAverage21Day]    [16.3967/16.4] 
[MovingAverage50Day]    [16.0764/16.11] 
[MovingAverage9Day]    [16.5478/16.59] 
[Volatility20Day]    [0.0385/0.146] 
[Volatility6Month]    [0.178/0.2494] 
[YTDRateOfReturn]    [0.5767/0.6011] 
[AverageVolume250]    [22544029/22428276] 
+0

具有匹配符号的行是否也与符号外部相同?它是您正在匹配的csv的特定列还是所有列?你可能可以使用'compare-object'来放弃很多。看到csvs的内容将会很有帮助 – 2014-10-07 17:33:10

+0

多久太久了? )无论如何,如果我是你,我会将表转储到数据库,然后让数据库引擎为我完成这项工作。作为免费的奖励,您可以运行任意的SELECT,而无需每次都编写和调试整个新脚本。 – 2014-10-07 17:42:27

+0

我已经添加了一个CSV示例以及当前代码的一些示例输出。一些文件可以在10分钟内完成,这是可以的。但是其中一些大文件可能需要一个小时或更长时间。 – 2014-10-07 18:07:51

回答

1

您至少需要PowerShell 3。0这个工作。虽然它可以更改为支持2.0

$firstData = Import-CSV C:\temp\sample.csv -Delimiter ";" | Group-Object -AsHashTable -AsString -Property Symbol 
$secondData = Import-CSV C:\temp\sample2.csv -Delimiter ";" | Group-Object -AsHashTable -AsString -Property Symbol 

$firstData.GetEnumerator() | ForEach-Object{ 
    If ($secondData.ContainsKey($_.Key)){ 
     $symbol = $_.Key 

     [PSCustomObject]@{ 
      'Symbol' = $symbol 
      'AverageVolume100Day' = "$($firstData[$symbol].AverageVolume100Day)/$($secondData[$symbol].AverageVolume100Day)" 
      'AverageVolume22Day' = "$($firstData[$symbol].AverageVolume22Day)/$($secondData[$symbol].AverageVolume22Day)" 
     } 
    } 
} 

该解决方案没有完全构建,但足以向您展示我正在尝试做什么。你可以做到这一点,以便你感兴趣的所有参数都被单独存储,而不是像我所做的AverageVolume100DayAverageVolume22Day那样拼写出来。此外,我没有足够的样本数据来执行此操作

这样做是将两个数据样本导入为CSV's并将数据转换为散列表,其中数据符号和其余数据是价值。

循环遍历每个符号并验证它是否与其他样本数据集中的匹配。如果找到匹配项,则会构建一个自定义对象,该对象包含每个样本数据集中的每个值,并与数据中的反斜杠相比较。

我从输出中忽略了标题,因为它让它重复似乎重复:)。我有没有的想法,如果这会更有效,但我会考虑试一试。

样品输出。

Symbol      AverageVolume100Day   AverageVolume22Day   
------      -------------------   ------------------   
AA       1.52891e+007/1.52891e+007 1.1017e+007/1.1017e+007 
A       1.81273e+006/1.81573e+006 1.85068e+006/1.85368e... 

在powershell中的输出可能不可读,某些列可能会被破坏。将此全部发送到Export-CSV将是一个选项。从评论

更新这是与具有动态头的额外的好处类似的解决方案。因为我还不满意,所以我需要对输出进行一些处理。

$firstData = Import-CSV C:\temp\sample.csv -Delimiter ";" | Group-Object -AsHashTable -AsString -Property Symbol 
$secondData = Import-CSV C:\temp\sample2.csv -Delimiter ";" | Group-Object -AsHashTable -AsString -Property Symbol 
$propertyNames = @("AverageVolume100Day","AverageVolume22Day","AnnualHighDate-Date") 
$properties = @{} 


$firstData.GetEnumerator() | ForEach-Object{ 
    If ($secondData.ContainsKey($_.Key)){ 
     $symbol = $_.Key 

     $properties.Symbol = $symbol 
     ForEach($property in $propertyNames) { 
      $properties.$property = "$($firstData[$symbol].$property)/$($secondData[$symbol].$property)" 
     } 
     New-Object Psobject -Property $properties 
    } 
} | Format-List 

使用数组$propertyNames根据需要填写标题。在ForEach-Loop周期通过每个和建立$properties。当你有很多头文件时,Format-List将使输出可读。

+0

非常感谢你的帮助!我会给这个试试:) – 2014-10-08 15:23:27

+0

我会看看如果我可以做出更动态的东西,但你需要做的就是为每个像''header'=“$($ firstData [$ symbol])添加另一行。头文件)/ $($ secondData [$ symbol] .header)“'。如果'header'包含一个空格,则将其包含在引号中。 – Matt 2014-10-08 18:02:02

+0

我有一些麻烦,把我的数组标题,并将它们添加到自定义对象,就像你手动使用'AverageVolume100Day''AverageVolume22Day',我敢肯定我的经验不足显示在这里,它的东西很简单.. 根据所比较的文件,它们将具有不同数量/类型的标题,因此能够在对象内动态更改它们是必须的。 – 2014-10-08 18:04:19

1

的规范回答这样的问题是使用查找表。有很多方法可以创建一个。一般方法如下。

计算来自第一个输入的每个数据行的散列值。将散列存储在容器中。在准备好查找表之后,逐行读取第二个文件并以相同的方式计算哈希值。检查查找表是否包含散列。如果没有,则会得到第一个文件中不存在的行。如果是这样,你就完全匹配了。

人们可以使用,比如说,MD5计算哈希。将散列存储在已排序的列表中,并使用二进制搜索来查找O(n log n)中的匹配项。更简单的选择是使用散列表(又名关联数组),它在窗帘后面进行散列计算。

在你的情况下,整个数据行的散列似乎不可行。生成查找表可能更合适,该查找表仅使用散列来查找相关行以供进一步处理。

至于如何创建查找表,请看another a post

+0

分隔谢谢!这是非常有帮助和信息的 – 2014-10-08 15:24:53