2014-10-07 82 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