Сравните числовой массив с существующими записями в Excel или VBA

У меня есть лист с 3 тысячами строк и 15 столбцами. Каждый столбец заполнен случайным числом от 1 до 25. Очень похоже на результаты лотереи, где каждый столбец представляет собой число, выпавшее из лотереи. (от 1-25)

Мне нужно сравнить, находится ли последовательность в строке 1 (для всех 3k+ строк) в какой-либо другой строке. Это означает, что результаты лотереи появлялись дважды. Загвоздка в том, что мяч 1 может появиться в любом из 15 столбцов.

Могу ли я поместить формулу Excel в следующий столбец? Или код VBA (в идеале) для сравнения?

С двумя столбцами... в R2 введите =TEXTJOIN(",",,C2:Q2), а затем в S2 введите =COUNTIF(R:R,E2). Скопируйте вниз. Все, что выше 1, имеет совпадение.

CLR 25.07.2024 11:28

Забыл упомянуть, я застрял в Excel 2016, в котором нет формулы «textjoin».

ROCA 25.07.2024 11:32

Может быть, это придирки, но ведь шарик 1 может появляться только в первом столбце данных (С)?

Tom Sharpe 25.07.2024 11:32

Оно может появиться в других столбцах, да.

ROCA 25.07.2024 11:33

В 2016 году вместо =TEXTJOIN вы могли вручную создать вспомогательный столбец, используя =C2&","&D2&","&E2&","...

CLR 25.07.2024 11:33

ок, но, как упоминал выше @Tom Sharpe, шар 1 может появиться в другом столбце

ROCA 25.07.2024 11:36

Это не должно иметь значения, при условии, что a. значения отсортированы одинаково (что предполагает ваш пример данных) и b. вы хотите сопоставить 15 значений с 15 значениями, а не с подмножествами.

CLR 25.07.2024 11:38

Если Bola2 (или выше) может содержать значение 1, это означает, что ваши данные не отсортированы — сначала вам нужно отсортировать данные. В этом случае опубликуйте снимок экрана с более реалистичными данными, которые подскажут нам, что может присутствовать.

CLR 25.07.2024 11:43

Как бы вы хотели вернуть найденную похожую строку?

FaneDuru 25.07.2024 12:27
Преобразование HTML-таблицы в профессиональный документ Excel
Преобразование HTML-таблицы в профессиональный документ Excel
Это самый простой способ создания Excel из HTML-таблицы.
Импорт excel в laravel в базу данных
Импорт excel в laravel в базу данных
Здравствуйте, дорогой читатель, в этой статье я расскажу практическим и быстрым способом, как импортировать файл Excel в вашу базу данных с помощью...
4
9
110
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Это всего лишь игрушечный пример, но вы можете попробовать что-то вроде этого, предполагая, что каждая строка расположена в порядке возрастания:

=SUM(--(MMULT(ABS(A2:C2-A$2:C$5),TRANSPOSE(COLUMN(A$2:C$5))^0)=0))

Пример без совпадений

Если ответ больше 1, имеется дубликат.

В Excel 2016 вам может потребоваться ввести его в массив или использовать Sumproduct.

Вот пример с совпадением

Вам нужно заблокировать эти A2:C4, иначе они улетят..

CLR 25.07.2024 11:55

Может быть более серьезная проблема, поскольку (я думаю) 1,2,5 будет соответствовать 1,3,4.

Tom Sharpe 25.07.2024 11:58

Я думаю, это исправит ситуацию.

Tom Sharpe 25.07.2024 12:03

Мне кажется, или это работает только с квадратными столами? (3х3, 4х4 и т. д.)

CLR 25.07.2024 12:13

Да, моя вина (честно говоря, я давно не использовал mmult таким образом) - скоро будет редактирование.

Tom Sharpe 25.07.2024 12:34

Хороший! Чистое колдовство, но приятно.

CLR 25.07.2024 12:40
Ответ принят как подходящий

Пожалуйста, попробуйте следующий код. Он должен выполнить задание за несколько секунд, в зависимости от вероятности отсутствия совпадений в первых столбцах:

Sub MatchFirstRowNumber()
 Dim ws As Worksheet, lastR As Long, rng As Range, arr
 Dim i As Long, j As Long, mtch, boolNo As Boolean
 
 Set ws = ActiveSheet
 lastR = ws.Range("A" & ws.rows.count).End(xlUp).row
 Set rng = ws.Range("A2:O" & lastR)
 arr = rng.rows(1).Value2 'place the first row in an array

 For i = 2 To rng.rows.count
    boolNo = True
    For j = 1 To UBound(arr, 2)
      mtch = Application.match(arr(1, j), rng.rows(i), 0)
      If IsError(mtch) Then boolNo = False: Exit For
    Next j
    If boolNo Then MsgBox "Row """ & i + 1 & """ contains the same nubmers as the first one!", vbInformation, "A match has been found"
 Next
End Sub

В качестве возврата он отправляет сообщение с упоминанием соответствующей строки...

Код можно адаптировать для (также) возврата строк с определенным количеством совпадений (например, 14...).

Или он может записать совпадающие строки и отправить в конце сообщение с их упоминанием.

Пожалуйста, оставьте отзыв после тестирования.

Отредактировано:

Следующая версия отправляет одно сообщение с перечислением всех совпадений:

Sub MatchFirstRowNumbers()
 Dim ws As Worksheet, lastR As Long, rng As Range, arr, arrRow
 Dim i As Long, j As Long, mtch, boolNo As Boolean, strMatches As String
 
 Set ws = ActiveSheet
 lastR = ws.Range("A" & ws.rows.count).End(xlUp).row
 Set rng = ws.Range("A2:O" & lastR)
 arr = rng.rows(1).Value2 'place the first row in an array
 
 strMatches = "The next matching rows have been found:" & vbCrLf
 For i = 2 To rng.rows.count
    boolNo = True
    For j = 1 To UBound(arr, 2)
      mtch = Application.match(arr(1, j), rng.rows(i), 0)
      If IsError(mtch) Then boolNo = False: Exit For
    Next j
    If boolNo Then strMatches = strMatches & "Row " & i + 1 & vbCrLf
 Next
 If strMatches <> "The next matching rows have been found:" & vbCrLf & vbCrLf Then MsgBox strMatches, vbInformation, "All matches"
End Sub

Второе редактирование:

Следующая версия еще быстрее. Он использует тот факт, что два массива могут быть сопоставлены напрямую, поэтому нет итераций между элементами ссылочного массива:

Sub MatchFirstRowNumbers()
 Dim ws As Worksheet, lastR As Long, rng As Range, arr, arrRow
 Dim i As Long, j As Long, arrMtch, boolNo As Boolean, strMatches As String
 
 Set ws = ActiveSheet
 lastR = ws.Range("A" & ws.rows.count).End(xlUp).row
 Set rng = ws.Range("A2:O" & lastR)
 arr = rng.rows(1).Value2 'place the first row in an array
 
 strMatches = "The next matching rows have been found:" & vbCrLf
 For i = 2 To rng.rows.count
    boolNo = True
    For j = 1 To UBound(arr, 2)
      arrMtch = Application.IfError(Application.match(arr, rng.rows(i).Value, 0), "X")  'it places "|" for not matching elements
      If Not IsError(Application.match("X", arrMtch, 0)) Then boolNo = False: Exit For 'if "X" exists change boolNo value and exist For
    Next j
    If boolNo Then strMatches = strMatches & "Row " & i + 1 & vbCrLf
 Next
 If strMatches <> "The next matching rows have been found:" & vbCrLf & vbCrLf Then MsgBox strMatches, vbInformation, "All matches"
End Sub

Я протестировал это на своих игрушечных данных (заменив A2:O на A2:C), и все работает отлично.

Tom Sharpe 25.07.2024 13:03

Весьма впечатляющий ответ от @FaneDuru, сработал хорошо, я отредактировал строку, как предложил Том Шарп, мои данные действительно начинаются в столбце C и заканчиваются в столбце Q... теперь, если не спрашивать слишком много, как бы я приступить к записи совпадений, например, на другом листе? Это не особо нужно, просто чтобы немного поучиться :)

ROCA 25.07.2024 13:38

@ROCA Для того, чтобы работать на другом листе достаточно правильно установить ws. Вместо Set ws = ActiveSheet следует использовать Set ws = Worksheets("Your sheet name"). Предположим, что соответствующий лист находится в активной книге. В противном случае вам следует квалифицировать его полностью. Я имею в виду что-то вроде Set ws = Workbooks("The Workbook name").Worksheets("Your sheet name")... А по поводу обрабатываемой ярости, я должен упомянуть, что код в его нынешнем виде работает с диапазоном, начинающимся со столбца A:A...

FaneDuru 25.07.2024 13:41

Спасибо @FaneDuru, я хотел записать совпадения, те же самые, которые появляются в диалоговом окне, когда оно находит совпадение. Я немного меняю ситуацию: для меньших диапазонов столбцов находит несколько совпадений, я хотел записать все совпадения...

ROCA 25.07.2024 13:44

@ROCA Не уверен, что понимаю, что вы имеете в виду... Хотите, чтобы код отправлял только одно сообщение, перечисляя все соответствующие строки? Если нет, то лучше опишите, что вам нужно.

FaneDuru 25.07.2024 13:46

Да, я понял, что вы начали с столбца А, я смог это отредактировать, без проблем.... еще раз спасибо

ROCA 25.07.2024 13:46

@ROCA В любом случае я отредактирую свой ответ и размещу другую адаптированную версию, чтобы отправлять только одно сообщение. Я думаю, будет удобнее, если будет найдено больше совпадений. Это легко, не более 2-3 минут...

FaneDuru 25.07.2024 13:54

@ROCA Сделал это. Пожалуйста, проверьте код после редактирования:... Конечно, то же самое упоминание относится к началу обрабатываемого диапазона (A:A, в моем коде...).

FaneDuru 25.07.2024 13:57

@ROCA Я нашел лучшее решение и опубликовал его после второго редактирования. В любом случае первая версия достаточно быстра, но в этой итерации между элементами ссылочного массива пропускаются. VBA может напрямую сопоставлять два массива и помещать номера ошибок для несовпадающих элементов.

FaneDuru 25.07.2024 15:47

Спасибо за интересный вызов! Хотя вы приняли ответ, я хотел увидеть способ сделать это без VBA и с помощью Excel 2016:

  • Добавьте вспомогательный столбец с отсортированным и объединенным текстом, к сожалению, его необходимо заполнить для всех строк
    =CONCAT(TEXT(SMALL(C2:Q2, COLUMN(C2:Q2) - COLUMN($C$2) + 1), "00"))
  • Еще один столбец для поиска дубликатов, снова заполнение
    =COUNTIF($S$2:$S$11, S2) > 1
  • Используйте условное форматирование, чтобы выделить повторяющиеся строки (не уверен, работает ли это в Excel 2016 — у меня нет возможности проверить); выделив первую ячейку в первой строке, введите первую ячейку из шага выше с фиксированной ссылкой на столбец и относительной ссылкой на строку.
    =$T2

Обозначьте строку, содержащую одинаковые целые числа

  • Для каждой строки столбец R будет заполнен разделенным запятыми списком (не менее двух чисел) индекса каждой строки (строки диапазона), содержащим те же 15 чисел. Результирующая ячейка становится пустой, если не найдена «соответствующая» строка.

  • К сожалению, комбинация Count/Match работает очень медленно при таком большом количестве сравнений. На выполнение задачи по поиску единственного набора из двух «совпадающих» строк потребовалось почти 4 минуты.

  • Пример данных был сгенерирован с использованием медленной, но относительно простой в использовании формулы MS365:

    =DROP(REDUCE("",SEQUENCE(3000),LAMBDA(rr,r,
        VSTACK(rr,TAKE(SORTBY(SEQUENCE(,25),RANDARRAY(,25)),,15)))),1)
    

    скопированы и вставлены как значения. Его можно воспроизвести с помощью быстрого макроса VBA (например, поиск случайного массива).

Sub TestMatch()
    
    Dim t As Double: t = Timer
    
    Const COLS_COUNT As Long = 15
    Const FIRST_ROW As Long = 2
    Const FIRST_COLUMN As Long = 3
    Const COLUMN_OFFSET As Long = 1
    Const DELIMITER As String = ", "
    
    Dim ws As Worksheet: Set ws = ActiveSheet
    Dim rg As Range: Set rg = ws.Range("A1").CurrentRegion
    
    Dim rCount As Long: rCount = rg.Rows.Count - 1
    
    Dim srg As Range:
    Set srg = rg.Resize(rCount, COLS_COUNT).Offset(FIRST_ROW - 1, FIRST_COLUMN - 1)
    
    Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
    
    Dim i As Long
    
    For i = 1 To rCount
        dict(i) = Empty
    Next i
    
    Dim srrg As Range, drrg As Range, Count As Long, j As Long
    
    With Application
        For i = 1 To rCount - 1
            If dict.Exists(i) Then
                For j = i + 1 To rCount
                    If dict.Exists(j) Then
                        Count = .Count(.Match(srg.Rows(i), srg.Rows(j), 0))
                        If Count = COLS_COUNT Then
                            If IsEmpty(dict(i)) Then
                                Set dict(i) = CreateObject("Scripting.Dictionary")
                            End If
                            dict(i)(j) = Empty
                            dict.Remove j
                        End If
                    End If
                Next j
            End If
        Next i
    End With
                    
    If dict.Count = rCount Then Exit Sub ' no duplicate rows found
    
    Debug.Print Timer - t
    
    Dim Data() As String: ReDim Data(1 To rCount, 1 To 1)
    
    Dim oKey As Variant, iKey As Variant, rStr As String
    
    For Each oKey In dict.Keys
        If Not IsEmpty(dict(oKey)) Then
            rStr = oKey
            rStr = rStr & DELIMITER & Join(dict(oKey).Keys, DELIMITER)
            Data(oKey, 1) = rStr
            For Each iKey In dict(oKey).Keys
                Data(iKey, 1) = rStr
            Next iKey
        End If
    Next oKey
        
    Dim drg As Range:
    Set drg = srg.Columns(COLS_COUNT).Offset(, COLUMN_OFFSET)
    drg.Value = Data
        
    Debug.Print Timer - t
        
End Sub

Другие вопросы по теме