web-dev-qa-db-ja.com

検索数を増やすためにvlookupを最適化する方法は? (VLOOKUPの代替)

関心のあるコンテキスト内でパフォーマンスが改善されたvlookupの代替手段を探しています。

コンテキストは次のとおりです。

  • 大きな{key; data}のデータセットがあります(〜100'000レコード)
  • データセットに対して多くのVLOOKUP操作を実行したい(通常は、データセット全体を並べ替える)
  • データセットに重複キーはありません
  • 完全一致のみを探しています(VLOOKUPの最後の引数はFALSEです)

説明するスキーマ:

参照シート:("sheet1"

        A           B
     1
     2  key1        data1
     3  key2        data2
     4  key3        data3
   ...  ...         ...
 99999  key99998    data99998
100000  key99999    data99999
100001  key100000   data100000
100002

ルックアップシート:

        A           B
     1
     2  key51359    =VLOOKUP(A2;sheet1!$A$2:$B$100001;2;FALSE)
     3  key41232    =VLOOKUP(A3;sheet1!$A$2:$B$100001;2;FALSE)
     4  key10102    =VLOOKUP(A3;sheet1!$A$2:$B$100001;2;FALSE)
   ...  ...         ...
 99999  key4153     =VLOOKUP(A99999;sheet1!$A$2:$B$100001;2;FALSE)
100000  key12818    =VLOOKUP(A100000;sheet1!$A$2:$B$100001;2;FALSE)
100001  key35032    =VLOOKUP(A100001;sheet1!$A$2:$B$100001;2;FALSE)
100002

2.67 GHzのCore i7 M 620では、これは約10分で計算されます

このコンテキストでパフォーマンスが向上したVLOOKUPの代替手段はありますか?

23
d-stroyer

次の代替案を検討しました。

  • VLOOKUP配列式
  • マッチ/インデックス
  • VBA(辞書を使用)

比較したパフォーマンスは次のとおりです。

  • VLOOKUPの単純な式:〜10分
  • VLOOKUP配列式:〜10分(1:1パフォーマンスインデックス)
  • MATCH/INDEX:〜2分(5:1パフォーマンスインデックス)
  • VBA(辞書を使用):〜6秒(100:1のパフォーマンスインデックス)

同じリファレンスシートを使用する

1)ルックアップシート:(vlookup array formula version)

         A          B
     1
     2   key51359    {=VLOOKUP(A2:A10001;sheet1!$A$2:$B$100001;2;FALSE)}
     3   key41232    formula in B2
     4   key10102    ... extends to
   ...   ...         ... 
 99999   key4153     ... cell B100001
100000   key12818    ... (select whole range, and press
100001   key35032    ... CTRL+SHIFT+ENTER to make it an array formula)
100002

2)ルックアップシート:(match + index version)

         A           B                                       C
      1
      2  key51359    =MATCH(A2;sheet1!$A$2:$A$100001;)       =INDEX(sheet1!$B$2:$B$100001;B2)
      3  key41232    =MATCH(A3;sheet1!$A$2:$A$100001;)       =INDEX(sheet1!$B$2:$B$100001;B3)
      4  key10102    =MATCH(A4;sheet1!$A$2:$A$100001;)       =INDEX(sheet1!$B$2:$B$100001;B4)
    ...  ...         ...                                     ...
  99999  key4153     =MATCH(A99999;sheet1!$A$2:$A$100001;)   =INDEX(sheet1!$B$2:$B$100001;B99999)
 100000  key12818    =MATCH(A100000;sheet1!$A$2:$A$100001;)  =INDEX(sheet1!$B$2:$B$100001;B100000)
 100001  key35032    =MATCH(A100001;sheet1!$A$2:$A$100001;)  =INDEX(sheet1!$B$2:$B$100001;B100001)
 100002

3)ルックアップシート:(vbalookupバージョン)

       A          B
     1
     2  key51359    {=vbalookup(A2:A50001;sheet1!$A$2:$B$100001;2)}
     3  key41232    formula in B2
     4  key10102    ... extends to
   ...  ...         ...
 50000  key91021    ... 
 50001  key42       ... cell B50001
 50002  key21873    {=vbalookup(A50002:A100001;sheet1!$A$2:$B$100001;2)}
 50003  key31415    formula in B50001 extends to
   ...  ...         ...
 99999  key4153     ... cell B100001
100000  key12818    ... (select whole range, and press
100001  key35032    ... CTRL+SHIFT+ENTER to make it an array formula)
100002

[〜#〜] nb [〜#〜]:何らかの(外部の内部)理由により、vbalookupは一度に65536を超えるデータを返すことができません。そのため、配列式を2つに分割する必要がありました。

および関連するVBAコード:

Function vbalookup(lookupRange As Range, refRange As Range, dataCol As Long) As Variant
  Dim dict As New Scripting.Dictionary
  Dim myRow As Range
  Dim I As Long, J As Long
  Dim vResults() As Variant

  ' 1. Build a dictionnary
  For Each myRow In refRange.Columns(1).Cells
    ' Append A : B to dictionnary
    dict.Add myRow.Value, myRow.Offset(0, dataCol - 1).Value
  Next myRow

  ' 2. Use it over all lookup data
  ReDim vResults(1 To lookupRange.Rows.Count, 1 To lookupRange.Columns.Count) As Variant
  For I = 1 To lookupRange.Rows.Count
    For J = 1 To lookupRange.Columns.Count
      If dict.Exists(lookupRange.Cells(I, J).Value) Then
        vResults(I, J) = dict(lookupRange.Cells(I, J).Value)
      End If
    Next J
  Next I

  vbalookup = vResults
End Function

注意:Scripting.DictionaryにはMicrosoft Scripting Runtimeへの参照が必要です。これは手動で追加する必要があります(Excel VBAウィンドウの[ツール]-> [参照]メニュー)

結論:

このコンテキストでは、ディクショナリを使用するVBAはVLOOKUPを使用するよりも100倍速く、MATCH/INDEXよりも20倍高速です。

22
d-stroyer

また、「double Vlookup」メソッドの使用を検討することもできます(私の考えではなく、他の場所で見られます)。シート1で説明したものと同じデータセットを使用して、シート2で100,000のルックアップ値(ランダムにソート)でテストし、4秒弱で時間を計りました。コードも少し単純です。

Sub FastestVlookup()

    With Sheet2.Range("B1:B100000")
        .FormulaR1C1 = _
        "=IF(VLOOKUP(RC1,Sheet1!R1C1:R100000C1,1)=RC1,VLOOKUP(RC1,Sheet1!R1C1:R100000C2,2),""N/A"")"
        .Value = .Value
    End With

End Sub
6
kevin9999

Excel 2013に切り替えて、データモデルを使用します。そこで、両方のテーブルで一意のIDキーを持つ列を定義し、ピボットテーブルでこれらの2つのテーブルをリレーションシップでバインドできます。どうしても必要な場合は、Getpivotdata()を使用して最初のテーブルを埋めることができます。同様の〜250K行テーブルでvlookupを実行する〜250K行テーブルがありました。 1時間後にExcelが計算を停止しました。 Data Modelを使用すると、10秒もかかりませんでした。

5
Alan