web-dev-qa-db-fra.com

Comment optimiser vlookup pour un nombre élevé de recherches? (alternatives à VLOOKUP)

Je recherche des alternatives à vlookup, avec des performances améliorées dans le contexte qui nous intéresse.

Le contexte est le suivant:

  • J'ai un ensemble de données de {clé; données} qui est gros (~ 100'000 enregistrements)
  • Je souhaite effectuer de nombreuses opérations de RECHERCHEV sur l'ensemble de données (l'utilisation typique consiste à réorganiser l'ensemble de données)
  • Mon ensemble de données n'a pas de clés en double
  • Je recherche uniquement les correspondances exactes (le dernier argument de VLOOKUP est FALSE)

Un schéma pour expliquer:

Fiche de référence: ("sheet1")

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

Feuille de recherche:

        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

Sur mon Core i7 M 620 @ 2,67 GHz, cela se calcule en ~ 10 minutes

Existe-t-il des alternatives à RECHERCHEV avec de meilleures performances dans ce contexte?

23
d-stroyer

J'ai considéré les alternatives suivantes:

  • Formule de tableau RECHERCHEV
  • MATCH/INDEX
  • VBA (à l'aide d'un dictionnaire)

La performance comparée est:

  • Formule simple RECHERCHEV: ~ 10 minutes
  • Formule de tableau RECHERCHEV: ~ 10 minutes (indice de performance 1: 1)
  • MATCH/INDEX: ~ 2 minutes (indice de performance 5: 1)
  • VBA (à l'aide d'un dictionnaire): ~ 6 secondes (indice de performance 100: 1)

Utilisation de la même feuille de référence

1) Feuille de recherche: (version de formule de tableau vlookup)

         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) Feuille de recherche: (version match + index)

         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) Feuille de recherche: (version 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 : Pour une raison (externe interne), vbalookup ne parvient pas à renvoyer plus de 65536 données à la fois. J'ai donc dû diviser la formule matricielle en deux.

et le code VBA associé:

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

NB: Scripting.Dictionary nécessite une référence à Microsoft Scripting Runtime qui doit être ajouté manuellement (menu Outils-> Références dans la fenêtre Excel VBA)

Conclusion :

Dans ce contexte, VBA utilisant un dictionnaire est 100 fois plus rapide que VLOOKUP et 20 fois plus rapide que MATCH/INDEX

22
d-stroyer

Vous pouvez également envisager d'utiliser la méthode "double Vlookup" (pas mon idée - vue ailleurs). Je l'ai testé sur 100 000 valeurs de recherche sur la feuille 2 (triées au hasard) avec un ensemble de données identique à celui que vous avez décrit sur la feuille 1, et je l'ai chronométré en un peu moins de 4 secondes. Le code est également un peu plus simple.

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

Basculez vers Excel 2013 et utilisez le modèle de données. Vous pouvez y définir une colonne avec des clés d'ID uniques dans les deux tables et lier ces deux tables avec une relation dans le tableau croisé dynamique. Si nécessaire, vous pouvez utiliser Getpivotdata () pour remplir le premier tableau. J'avais une table de ~ 250K lignes faisant vlookup dans la table de ~ 250K lignes similaire. Excel arrêté le calcul après une heure. Avec le modèle de données, il a fallu moins de 10 secondes.

5
Alan