【问题标题】:Excel Highlight Duplicates and Filter by color alternativeExcel突出显示重复项并按颜色替代过滤
【发布时间】:2017-03-04 08:54:39
【问题描述】:

我的电子表格有大约 800,000 行和 30 列。客户只对一列中的重复值感兴趣。他们需要整排回来。例如

MemberId|Name|Address|CircleScore
H111|John Doe|123 W Main|2.4
H222|Jane Doe|124 W Main|3.2
H333|Bob Doe|125 W Main|2.5
H444|Jake Doe|126 W Main|2.1
H555|Mike Doe|127 W Main|2.4

他们想要在 CircleScore 中有重复的整行。所以我过滤的excel应该只包含:

MemberId|Name|Address|CircleScore
H111|John Doe|123 W Main|2.4
H555|Mike Doe|127 W Main|2.4

我尝试突出显示重复的 CircleScore 和过滤,但过滤部分需要永远。我已经等了 15 分钟,但仍然没有运气。副本可能在 150k 左右。

还有其他选择吗?

【问题讨论】:

    标签: excel


    【解决方案1】:

    我会创建一个Is_Duplicated 指示列并使用它来过滤重复的CircleScores


    更新(每 cmets):

    或者,您可以sort CircleScore 列并减少公式对系​​统的负担(注意CircleScore 必须事先排序):

    【讨论】:

    • 我喜欢你使用 COUNTIFS(D:D,D2)>1 的方法;通常在检查重复项时我使用类似 ISERROR(MATCH(D2,$D$1:D1,0)) 的东西。我认为你的更简单,尤其是在视觉上。但是请注意,这仅在 any 重复值需要特别注意时才有效,而不仅仅是第二个、第三个等值[即:如果您希望以一种方式处理第一次迭代,而第二次等. 迭代以另一种方式处理,MATCH 或类似方法可能更适合]。
    • 谢谢。又慢得令人难以置信。我不得不杀了它。双击拖动所有单元格的公式是罪魁祸首。 700,000 x 30 对于 excel 来说实在是太多了,嗯。我有 i7 3.4 GHz 和 16 GB 的 RAM。
    • @user3726933 您是否考虑过 PowerPivot 或在使用 SQL 的数据库中执行此操作?如果界面仍然必须是 Excel,我可能会尝试在其他地方创建指标列,然后将数据加载回 Excel。或者,您可以先sort CircleScore 数据,然后修改应该不那么繁重的公式(请参阅我的编辑)。
    【解决方案2】:

    如果您是 a) 按小时计酬但觉得薪水过低,b) 计划在例行处理期间小睡,或 c) a) 和 b) 两者,请忽略此提交。

    对于任何接近 800K 行(30 列)的数据集,您都会想要进入变体数组领域。由于处理工作表值所需的时间通常为 5-7%,因此非常适合大型数据块。

    每当“重复”一词出现时,我都会立即开始思考Scripting.Dictionary 对象在其Keys 上的唯一索引如何受益。在这个解决方案中,我使用了一对字典来识别具有重复 Circle Score 值的数据行。

    2400 万个数据单元需要存储和传输。批量方法每次都击败单个方法,剥离数据的最大方法是将所有 800K 行 × 30 列填充到一个变体数组中。所有处理都在内存中,结果返回到报告工作表en masse

    isolateDuplicateCircleScores代码

    Sub isolateDuplicateCircleScores()
        Dim d As Long, v As Long, csc As Long, stmp As String
        Dim ky As Variant, itm As Variant, vVALs As Variant, dCSs As Object, dDUPs As Object
        Dim w As Long, vWSs As Variant
        'early binding
        'dim dCSs As new scripting.dictionary, dDUPs As new scripting.dictionary
    
        appTGGL bTGGL:=False
    
        'late binding - not necessary with Early Binding (see footnote ¹)
        Set dCSs = CreateObject("Scripting.Dictionary")
        Set dDUPs = CreateObject("Scripting.Dictionary")
    
        'set to the defaults (not necessary)
        dCSs.comparemode = vbBinaryCompare
        dDUPs.comparemode = vbBinaryCompare
    
        'for testing on multiple row number scenarios
        'vWSs = Array("CircleScores_8K", "CircleScores_80K", "CircleScores_800K")
        'for runtime
        vWSs = Array("CircleScores")  '<~~ your source worksheet here
    
        For w = LBound(vWSs) To UBound(vWSs)
            'ThisWorkbook.Save
            Debug.Print vWSs(w)
            Debug.Print Timer
            With Worksheets(vWSs(w))
    
                On Error Resume Next
                Worksheets(vWSs(w) & "_dupes").Delete
                On Error GoTo 0
    
                ReDim vVALs(0)
                dCSs.RemoveAll
                dDUPs.RemoveAll
    
                'prep a new worksheet to receive the duplicates
                .Cells(1, 1).CurrentRegion.Resize(2).Copy
                With Worksheets.Add(after:=Worksheets(.Index))
                    .Name = vWSs(w) & "_dupes"
                    With .Cells(1, 1)
                        .PasteSpecial Paste:=xlPasteAllUsingSourceTheme, Operation:=xlNone
                        .PasteSpecial Paste:=xlPasteColumnWidths, Operation:=xlNone
                        .Value = .Value2
                        .Offset(1, 0).EntireRow.ClearContents
                    End With
                End With
                'finish prep with freeze row 1 and zoom to 80%
                With Application.Windows(1)
                    .SplitColumn = 0
                    .SplitRow = 1
                    .FreezePanes = True
                    .Zoom = 80
                End With
    
                'grab all of the data into a variant array
                ReDim vVALs(0)
                csc = Application.Match("CircleScore", .Rows(1), 0) 'CircleScore column number needed later
                vVALs = .Range(.Cells(2, 1), _
                               .Cells(.Cells(Rows.Count, csc).End(xlUp).Row, _
                                      .Cells(1, Columns.Count).End(xlToLeft).Column)).Value2
                'Debug.Print LBound(vVALs, 1) & ":" & UBound(vVALs, 1)  '1:~800K
                'Debug.Print LBound(vVALs, 2) & ":" & UBound(vVALs, 2)  '1:~30
            End With    'done with the original worksheet
    
            'populate the dDUPs dictionary using the key index in dCSs
            For v = LBound(vVALs, 1) To UBound(vVALs, 1)
                If dCSs.exists(vVALs(v, csc)) Then
                    stmp = vVALs(v, 1)
                    For d = LBound(vVALs, 2) + 1 To UBound(vVALs, 2)
                        stmp = Join(Array(stmp, vVALs(v, d)), ChrW(8203))
                    Next d
                    dDUPs.Add Key:=v, Item:=stmp
                    If Not dDUPs.exists(dCSs.Item(vVALs(v, csc))) Then
                        stmp = vVALs(dCSs.Item(vVALs(v, csc)), 1)
                        For d = LBound(vVALs, 2) + 1 To UBound(vVALs, 2)
                            stmp = Join(Array(stmp, vVALs(dCSs.Item(vVALs(v, csc)), d)), ChrW(8203))
                        Next d
                        dDUPs.Add Key:=dCSs.Item(vVALs(v, csc)), Item:=stmp
                    End If
                Else
                    dCSs.Item(vVALs(v, csc)) = v
                End If
            Next v
    
            'split the dDUPs dictionary items back into a variant array
            d = 1
            ReDim vVALs(1 To dDUPs.Count, 1 To UBound(vVALs, 2))
            For Each ky In dDUPs.keys
                itm = Split(dDUPs.Item(ky), ChrW(8203))
                For v = LBound(itm) To UBound(itm)
                    vVALs(d, v + 1) = itm(v)
                Next v
                d = d + 1
            Next ky
    
            'put the values into the duplicates worksheet
            With Worksheets(vWSs(w) & "_dupes")
                .Cells(2, 1).Resize(UBound(vVALs, 1), UBound(vVALs, 2)) = vVALs
                With .Cells(1, 1).CurrentRegion
                    With .Resize(.Rows.Count - 1, .Columns.Count).Offset(1, 0)
                        .Rows(1).Copy
                        .PasteSpecial Paste:=xlPasteFormats, Operation:=xlNone
                    End With
                    .Cells.Sort Key1:=.Columns(csc), Order1:=xlAscending, _
                                Key2:=.Columns(1), Order2:=xlAscending, _
                                Orientation:=xlTopToBottom, Header:=xlYes
                End With
            End With
    
            Debug.Print Timer
        Next w
    
        dCSs.RemoveAll: Set dCSs = Nothing
        dDUPs.RemoveAll: Set dDUPs = Nothing
    
        appTGGL
    End Sub
    
    Public Sub appTGGL(Optional bTGGL As Boolean = True)
        With Application
            .ScreenUpdating = bTGGL
            .EnableEvents = bTGGL
            .DisplayAlerts = bTGGL
            .AutoRecover.Enabled = bTGGL   'no interruptions with an auto-save
            .Calculation = IIf(bTGGL, xlCalculationAutomatic, xlCalculationManual)
            .CutCopyMode = False
            .StatusBar = vbNullString
        End With
        Debug.Print Timer
    End Sub
    

    Sample Data and Results

      
            800K 行 × 30 列随机样本数据

      
            ~123K 行 × 30 列重复行(排序和格式化大约需要一分半钟)

    Timed Results

    tbh,我从来没有在旧笔记本电脑上安装 32 位版本的 Excel,在不重新启动 Excel 的情况下多次运行 800K 通行证。重新启动后,结果与显示的一致。 64 位 Excel 反复运行,没有任何问题。

            

    大型工作表附录

    在处理包含大数据块的工作表时,有一些一般性的改进可以限制您的等待时间。您将 Excel 用作中型数据库工具,因此请将数据工作表视为应有的原始数据。

    • 如果您使用的不是 64 位版本的 Excel,那么您所做的一切都是在浪费时间。见What version of Office am I using?Choose the 32-bit or 64-bit version of Office
    • 另存为 Excel 二进制工作簿(例如 .XLSB)。文件大小通常为原始文件的 25-35%。加载时间得到改善,一些计算更快(抱歉,后者没有经验定时数据)。一些会导致 .XLSX 或 .XLSM 崩溃的操作可以在 .XLSB 上正常运行。
    • 在工作簿的选项中禁用自动保存/自动恢复。 ([alt]+F、T、S、[alt]+D、[OK])。当您尝试做某事时,没有什么比等待自动保存完成更令人恼火的了。当想要保存时,习惯Ctrl+S
    • 不惜一切代价避免易变函数¹;特别是在整个数据范围内使用的公式中。 COUNTIF 公式中的单个TODAY() 填充行的范围会让您经常坐在拇指上。
    • 说到公式,尽可能将所有公式恢复为其结果值。
    • 合并单元格、条件格式、数据验证以及使用格式和样式使单元格看起来漂亮会减慢您的速度。尽量减少使用任何带走原始数据的东西。并不像任何人实际上会查看 80 万行数据。
    • 删除数据后,在空白单元格上使用主页 ► 编辑 ► 清除 ► 全部清除。点击 Del 只会清除内容,可能不会重置 Worksheet.UsedRange property; Clear All 将有助于在下次保存时重置 .Used Range。
    • 如果您的计算机使用了一种或多种 Excel [无响应] 方案,请重新启动计算机。 Excel 永远不会从这些问题中完全恢复,并且简单地重新启动 Excel 以重新开始会更慢,并且以后更有可能进入相同的无响应条件。

    ¹ 如果您可以将 Scripting.Dictionary 的后期绑定转换为早期绑定,则必须将 Microsoft Scripting Runtime 添加到 VBE 的工具 ► 参考中。

    ² 当整个工作簿中的任何内容发生变化时,可变函数都会重新计算,而不仅仅是在影响其结果的某些内容发生变化时。 volatile 函数的示例有 INDIRECTOFFSETTODAYNOWRANDRANDBETWEENCELLINFO 工作表函数的一些子函数也会使它们变得易变。

    【讨论】:

    • 随机样本数据工作簿(XLSB)暂时为here
    【解决方案3】:

    试试这个 Vba 代码(并学习一点荷兰语)

    Sub DuplicatesInColumn()
    'maakt een lijst met de aangetroffen dubbelingen
    Dim LaatsteRij As Long
    Dim MatchNr As Long
    Dim iRij, iKolom, iTeller, Teller As Long, ControlKolom As Long
    iRij = 1
    
    iKolom = 5                   'number of columns in the sheet, Chance if not correct
    ControlKolom = 4             'column number where to find the doubles, Chance if not correct
    
    LaatsteRij = Cells(65000, iKolom).End(xlUp).Row: iTeller = iKolom
    
    Sheet1.Activate
    For iRij = 1 To LaatsteRij
        If Cells(iRij, ControlKolom) <> "" Then
            MatchNr = WorksheetFunction.Match(Cells(iRij, ControlKolom), Range(Cells(1, ControlKolom), Cells(LaatsteRij, ControlKolom)), 0)
        If iRij <> MatchNr Then
        iTeller = iKolom
        For Teller = 1 To iTeller
          Cells(iRij, iKolom + Teller).Offset(0, 2).Value = Range(Cells(iRij, Teller), Cells(iRij, Teller)).Value
        Next Teller
        End If: End If
    Next
    End Sub
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-18
      • 2020-10-12
      • 2021-05-07
      • 1970-01-01
      • 1970-01-01
      • 2014-05-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多