У меня есть лист с 3 тысячами строк и 15 столбцами. Каждый столбец заполнен случайным числом от 1 до 25. Очень похоже на результаты лотереи, где каждый столбец представляет собой число, выпавшее из лотереи. (от 1-25)
Мне нужно сравнить, находится ли последовательность в строке 1 (для всех 3k+ строк) в какой-либо другой строке. Это означает, что результаты лотереи появлялись дважды. Загвоздка в том, что мяч 1 может появиться в любом из 15 столбцов.
Могу ли я поместить формулу Excel в следующий столбец? Или код VBA (в идеале) для сравнения?
Забыл упомянуть, я застрял в Excel 2016, в котором нет формулы «textjoin».
Может быть, это придирки, но ведь шарик 1 может появляться только в первом столбце данных (С)?
Оно может появиться в других столбцах, да.
В 2016 году вместо =TEXTJOIN
вы могли вручную создать вспомогательный столбец, используя =C2&","&D2&","&E2&","
...
ок, но, как упоминал выше @Tom Sharpe, шар 1 может появиться в другом столбце
Это не должно иметь значения, при условии, что a. значения отсортированы одинаково (что предполагает ваш пример данных) и b. вы хотите сопоставить 15 значений с 15 значениями, а не с подмножествами.
Если Bola2 (или выше) может содержать значение 1, это означает, что ваши данные не отсортированы — сначала вам нужно отсортировать данные. В этом случае опубликуйте снимок экрана с более реалистичными данными, которые подскажут нам, что может присутствовать.
Как бы вы хотели вернуть найденную похожую строку?
Это всего лишь игрушечный пример, но вы можете попробовать что-то вроде этого, предполагая, что каждая строка расположена в порядке возрастания:
=SUM(--(MMULT(ABS(A2:C2-A$2:C$5),TRANSPOSE(COLUMN(A$2:C$5))^0)=0))
Пример без совпадений
Если ответ больше 1, имеется дубликат.
В Excel 2016 вам может потребоваться ввести его в массив или использовать Sumproduct.
Вот пример с совпадением
Вам нужно заблокировать эти A2:C4, иначе они улетят..
Может быть более серьезная проблема, поскольку (я думаю) 1,2,5 будет соответствовать 1,3,4.
Я думаю, это исправит ситуацию.
Мне кажется, или это работает только с квадратными столами? (3х3, 4х4 и т. д.)
Да, моя вина (честно говоря, я давно не использовал mmult таким образом) - скоро будет редактирование.
Хороший! Чистое колдовство, но приятно.
Пожалуйста, попробуйте следующий код. Он должен выполнить задание за несколько секунд, в зависимости от вероятности отсутствия совпадений в первых столбцах:
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), и все работает отлично.
Весьма впечатляющий ответ от @FaneDuru, сработал хорошо, я отредактировал строку, как предложил Том Шарп, мои данные действительно начинаются в столбце C и заканчиваются в столбце Q... теперь, если не спрашивать слишком много, как бы я приступить к записи совпадений, например, на другом листе? Это не особо нужно, просто чтобы немного поучиться :)
@ROCA Для того, чтобы работать на другом листе достаточно правильно установить ws
. Вместо Set ws = ActiveSheet
следует использовать Set ws = Worksheets("Your sheet name")
. Предположим, что соответствующий лист находится в активной книге. В противном случае вам следует квалифицировать его полностью. Я имею в виду что-то вроде Set ws = Workbooks("The Workbook name").Worksheets("Your sheet name")
... А по поводу обрабатываемой ярости, я должен упомянуть, что код в его нынешнем виде работает с диапазоном, начинающимся со столбца A:A...
Спасибо @FaneDuru, я хотел записать совпадения, те же самые, которые появляются в диалоговом окне, когда оно находит совпадение. Я немного меняю ситуацию: для меньших диапазонов столбцов находит несколько совпадений, я хотел записать все совпадения...
@ROCA Не уверен, что понимаю, что вы имеете в виду... Хотите, чтобы код отправлял только одно сообщение, перечисляя все соответствующие строки? Если нет, то лучше опишите, что вам нужно.
Да, я понял, что вы начали с столбца А, я смог это отредактировать, без проблем.... еще раз спасибо
@ROCA В любом случае я отредактирую свой ответ и размещу другую адаптированную версию, чтобы отправлять только одно сообщение. Я думаю, будет удобнее, если будет найдено больше совпадений. Это легко, не более 2-3 минут...
@ROCA Сделал это. Пожалуйста, проверьте код после редактирования:... Конечно, то же самое упоминание относится к началу обрабатываемого диапазона (A:A, в моем коде...).
@ROCA Я нашел лучшее решение и опубликовал его после второго редактирования. В любом случае первая версия достаточно быстра, но в этой итерации между элементами ссылочного массива пропускаются. VBA может напрямую сопоставлять два массива и помещать номера ошибок для несовпадающих элементов.
Спасибо за интересный вызов! Хотя вы приняли ответ, я хотел увидеть способ сделать это без VBA и с помощью Excel 2016:
=CONCAT(TEXT(SMALL(C2:Q2, COLUMN(C2:Q2) - COLUMN($C$2) + 1), "00"))
=COUNTIF($S$2:$S$11, S2) > 1
=$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
С двумя столбцами... в R2 введите
=TEXTJOIN(",",,C2:Q2)
, а затем в S2 введите=COUNTIF(R:R,E2)
. Скопируйте вниз. Все, что выше 1, имеет совпадение.