Üdvözletem,
Az ismétlődések (duplikációk) azonosítása/eltávolítása gyakori probléma, írtam is már róla egy bejegyzést, mely itt olvasható.
De mi van akkor, ha:
– a duplikációkat tartalmazó cellákat ki akarjuk színezni (vizuális ellenőrzés végett) és
– mindezt egy olyan oszlopban, mely 500.000 sort tartalmaz?
Aki próbált már Excelben több százezer vagy akár egy millió sort hozzáadni, az tudja, hogy könnyen megnövekszik a fájlméret, a munkafüzet lomha és lassú lesz, esetleg le is fagy.
Teszteljük:
– üres munkalapon az A1-es legyen az aktív cella
– jelöljük ki az „A” oszlopot
– írjunk be mondjuk 1-et, majd CTRL + ENTER
Most az „A” oszlop minden cellájában 1-es szerepel. Elmentjük, bezárjuk a fájlt és a mérete máris 5,4 MB. Képzeljük el ugyanezt több tucat oszloppal.
Vagyis az Excelben (2007-től) az 1.048.576 sor és a 16.384 oszlop erősen elméleti maximum.
Az alábbiakban olyan lehetőségeket ismertetek, mely az 500.000 adatsoros oszlopban kijelöli a duplikációkat. Kiinduló állapot: véletlenszámokkal töltöttem fel a C2:C5000001-es tartományt.
A következő metódusokat mindenképpen úgy próbáld ki, hogy csak az adott munkafüzet van megnyitva, mivel – az utolsó két módszer kivételével – nagy valószínűséggel szanaszét fog fagyni a munkafüzet és vele együtt az egész Excel is!
Módszerek:
I. a legkézenfekvőbbnek tűnő Feltételes formázás:
C2:C500001-es tartomány kijelölése -> Kezdőlap menü -> Feltételes formázás -> Cellakiemelési szabályok -> Ismétlődő értékek:
Majd itt OK:
Pár másodperc gondolkodás után pár cella piros háttérszínt kap, minden jónak tűnik. Azonban ha görgetjük a képernyőt, szaggat. Ennek oka, hogy a feltételes formázás folyamatosan kiértékelődik. Görgetés helyett tegyünk rá egy szűrőt és próbáljuk meg leszűrni a piros színű cellákra. A szűrés ikonra kattintva lefagy az Excel, a processzor felpörög, a régebbi gépeknek olyan hangja lesz a ventilátor miatt, mintha fel akarnának szállni 🙂
Ez tehát nem nyert, nézzük a következő opciókat.
II. Power Query-vel ezen link alapján (angolul)
Ezen most nem megyünk végig lépésről lépésre. A lelke az egésznek a csoportosítás PQ-ben, jelen esetben a számokat tartalmazó – itt „Véletlenszámok” nevű – oszlop soraié, mely az ismétlődéseket írja ki a „Duplicates” oszlopban:
Majd az oda-vissza, ún. kibontás nyílra kattintva (előző képen piros kerettel jelölve) visszarakjuk az „Index” oszlopot, hogy növekvő rendezéssel visszakapjuk a „Véletlenszám” oszlop sorainak eredeti sorrendjét:
Majd az „Index” oszlopot töröljük és a „Duplicates” oszlopban kivesszük a 0 értékeket, mivel ennél nincsenek ismétlődések:
Végül a PQ betölti a kimeneti adatokat egy táblázatba, az eredmény így néz ki:
Ezután már csak a cellák színezése marad.
Persze a „Duplicates” oszlopra nincs szükségünk – vagyis ezt is törölhetnénk PQ-ben – csupán az ellenőrzés végett maradt bent.
A Power Query képes kezelni több millió, vagy akár több tízmillió sort is. Gondolhatnánk, ez az 500.000 sor meg sem kottyan neki. Azonban ha frissítjük a PQ lekérdezést Excelben, meglepő módon 25-30 másodpercig homokórázik, mire frissíti a lekérdezést és ezzel együtt a munkalapon lévő táblázatot.
III. Irányított szűrés: itt csak pár értékkel dolgozom
– E1-es cellába írjunk be egy nevet, pl.: Feltétel
– E2-es cellába képlet a duplikációk azonosítására -> =DARABTELI(C:C;C2)<>1
Most itt járunk, a sárga színű cellák tartalmazzák a duplikációkat:
Ezután az aktív cella legyen a „C” oszlopban, majd:
Adatok menü -> Irányított: majd az alábbi négy opciót beállítani és OK.
Eredmény a „G” oszlopban:
Ha a fenti lépéseket 500.000 sorra engedjük rá, akkor lefagy az Excel, én legalábbis fél óra után lőttem le az Excelt a Feladatkezelőből.
IV. VBA megoldás #1
A dictionary objektum segítségével a futási idő ez esetben nálam 30-35 másodperc. Itt a „Adatok_Original” lapról a kód átmásolja az adatokat az „Adatok” lapra, majd ez utóbbin végzi el a színezést a „C” oszlopban. Eredmény:
Kód:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
Sub Duplication_Color_V01() Dim D As Object, i As Long, Rng As Range 'run time: cca 30-35 sec. 'col. "C" ("Értékek") for red cells on sheet "Adatok" after macro ran: 98.292.065.981 Application.ScreenUpdating = False If Not (Evaluate("isref(Adatok!A1)")) Then Worksheets.Add(before:=Worksheets(Worksheets.Count)).Name = "Adatok" Worksheets("Adatok").Select If ActiveSheet.AutoFilterMode = True Then ActiveSheet.AutoFilterMode = False Worksheets("Adatok_Original").Cells.Copy Worksheets("Adatok").Cells(1, 1) Set D = CreateObject("scripting.dictionary") 'Set Rng = Sheet5.Range("C1").CurrentRegion.Columns(2) Set Rng = Worksheets("Adatok").Range("C1").CurrentRegion.Columns(1) Rng.Interior.Pattern = xlNone For i = 2 To Rng.Rows.Count D(CStr(Rng.Cells(i, 1))) = D(CStr(Rng.Cells(i, 1))) + 1 Next i For i = 2 To Rng.Rows.Count If D(CStr(Rng.Cells(i, 1))) > 1 Then Rng.Cells(i, 1).Interior.Color = vbRed End If Next i Application.ScreenUpdating = True End Sub |
V. VBA megoldás #2
Érezzük, hogy az előző pontnál elért 30-35 másodpercen még lehetne javítani. Vannak üdítő kivételek, mikor a barkácsmegoldás („quick and dirty) – vagy legalábbis annak látszó – metódus bizonyul a leghatékonyabbnak.
Lényege:
– új oszlop („Index”) hozzáadása
– „Értékek” oszlop sorba rendezése (pl. növekvő sorrend)
– új oszlopban („Dupl”) képlet hozzáadása a duplikációk azonosítása végett: ha a cella tartalma egyenlő az alatta vagy a felette lévő cella tartalmával, akkor az eredmény „Igen”
– előző pontban lévő oszlopot leszűrni az „Igen”-ekre, majd beszínezni a leszűrt cellákat
– szűrő kikapcsolása, majd a segédoszlopok törlése
Az eredmény ugyanaz, mint az előző VBA-s megoldásnál, viszont a futási idő mindössze 6-8 másodperc lett!
Kód:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
Sub Duplication_Color_V11_Best() Dim Rng As Range 'shortest code 'run time: cca 6-8 sec! 'col. "C" ("Értékek") for red cells on sheet "Adatok" after macro ran: 98.292.065.981 'Steps: '1. add col. "B" ("Index") '2. Sort col. "C" ("Értékek") '3. add col. "D" ("Dupl."): IF function -> get only values '4.filter to col. "D" to "Yes" -> color filtered rows Application.ScreenUpdating = False If Not (Evaluate("isref(Adatok!A1)")) Then Worksheets.Add(before:=Worksheets(Worksheets.Count)).Name = "Adatok" Worksheets("Adatok").Select If ActiveSheet.AutoFilterMode = True Then ActiveSheet.AutoFilterMode = False Worksheets("Adatok_Original").Cells.Copy Worksheets("Adatok").Cells(1, 1) Range("B1:D1").Cells(1, 1).Resize(, 3) = Array(0, "Értékek", "Dupl.") Set Rng = Range("C1").CurrentRegion Rng.Columns(1).DataSeries Rowcol:=xlColumns, Type:=xlLinear, Date:=xlDay, Step:=1, Stop:=Rng.Rows.Count - 1, Trend:=False Rng.Sort Key1:=Rng.Columns(2), Header:=xlYes, Order1:=xlAscending 'col. "C" ("Értékek") 'Rng.Columns(2).Interior.Pattern = xlNone Range(Rng.Columns(3).Cells(2), Cells(Rng.Rows.Count, Rng.Cells(Rng.Cells.Count).Column)).FormulaR1C1 = "=IF(OR(RC[-1]=R[-1]C[-1],RC[-1]=R[1]C[-1]),""Igen"",""Nem"")" 'col. "D" ("Dupl.") without header Range(Rng.Columns(3).Cells(2), Cells(Rng.Rows.Count, Rng.Cells(Rng.Cells.Count).Column)) = Range(Rng.Columns(3).Cells(2), Cells(Rng.Rows.Count, Rng.Cells(Rng.Cells.Count).Column)).Value Rng.AutoFilter Rng.Columns.Count, "Igen", xlFilterValues Range(Rng.Columns(2).Cells(2), Cells(Rng.Rows.Count, Rng.Columns(2).Cells(1).Column)).Interior.Color = vbRed 'filtered col. "C" ("Értékek") without header If ActiveSheet.AutoFilterMode = True Then ActiveSheet.AutoFilterMode = False Rng.Sort Key1:=Range(Rng.Columns(1).Address), Header:=xlYes, Order1:=xlAscending 'col. "B" ("Index"): set original order back Union(Range("B:B"), Range("D:D")).Clear Application.ScreenUpdating = True End Sub |