programing

각 열 셀에 대한 열 셀 확장

css3 2023. 6. 24. 09:28

각 열 셀에 대한 열 셀 확장

3개의 서로 다른 데이터 집합이 있습니다(다른 열에 있음).

  1. 열 A에 있는 동물(5가지 종류)
  2. B열 과일(1000가지 종류)
  3. 열 C에 있는 국가(10가지 종류)

이 3개의 데이터 수집을 통해 총 50k에 해당하는 col의 요소에 대해 5x1000x10을 받고 싶습니다.EFG(각 과일과 각 국가에 해당하는 각 동물).

값을 수동으로 복사하여 붙여넣는 방식으로 수행할 수 있지만 시간이 오래 걸립니다.VBA 코드로 자동화할 수 있는 방법이 있습니까?

위에 제시된 것처럼 무제한 데이터 세트에 대한 보편적인 공식이 있습니까?명확하지 않은 사항이 있으면 알려주시기 바랍니다.

다음은 데이터의 작은 예와 결과가 어떻게 나타나는지 보여줍니다.

        Expanding data sets for each in other

저는 보편적으로 이것이 임의의 수의 열과 각각의 항목을 수용하기를 원한다고 생각합니다.몇 가지 변형 배열은 각 값에 대한 반복 주기를 계산하는 데 필요한 치수를 제공해야 합니다.

Option Explicit

Sub main()
    Call for_each_in_others(rDATA:=Worksheets("Sheet3").Range("A3"), bHDR:=True)
End Sub

Sub for_each_in_others(rDATA As Range, Optional bHDR As Boolean = False)
    Dim v As Long, w As Long
    Dim iINCROWS As Long, iMAXROWS As Long, sErrorRng As String
    Dim vVALs As Variant, vTMPs As Variant, vCOLs As Variant

    On Error GoTo bm_Safe_Exit
    appTGGL bTGGL:=False

    With rDATA.Parent
        With rDATA(1).CurrentRegion
            'Debug.Print rDATA(1).Row - .Cells(1).Row
            With .Resize(.Rows.Count - (rDATA(1).Row - .Cells(1).Row), .Columns.Count).Offset(2, 0)
                sErrorRng = .Address(0, 0)
                vTMPs = .Value2
                ReDim vCOLs(LBound(vTMPs, 2) To UBound(vTMPs, 2))
                iMAXROWS = 1
                'On Error GoTo bm_Output_Exceeded
                For w = LBound(vTMPs, 2) To UBound(vTMPs, 2)
                    vCOLs(w) = Application.CountA(.Columns(w))
                    iMAXROWS = iMAXROWS * vCOLs(w)
                Next w

                'control excessive or no rows of output
                If iMAXROWS > Rows.Count Then
                    GoTo bm_Output_Exceeded
                ElseIf .Columns.Count = 1 Or iMAXROWS = 0 Then
                    GoTo bm_Nothing_To_Do
                End If

                On Error GoTo bm_Safe_Exit
                ReDim vVALs(LBound(vTMPs, 1) To iMAXROWS, LBound(vTMPs, 2) To UBound(vTMPs, 2))
                iINCROWS = 1
                For w = LBound(vVALs, 2) To UBound(vVALs, 2)
                    iINCROWS = iINCROWS * vCOLs(w)
                    For v = LBound(vVALs, 1) To UBound(vVALs, 1)
                        vVALs(v, w) = vTMPs((Int(iINCROWS * ((v - 1) / UBound(vVALs, 1))) Mod vCOLs(w)) + 1, w)
                    Next v
                Next w
            End With
        End With
        .Cells(2, UBound(vVALs, 2) + 2).Resize(1, UBound(vVALs, 2) + 2).EntireColumn.Delete
        If bHDR Then
            rDATA.Cells(1, 1).Offset(-1, 0).Resize(1, UBound(vVALs, 2)).Copy _
                Destination:=rDATA.Cells(1, UBound(vVALs, 2) + 2).Offset(-1, 0)
        End If
        rDATA.Cells(1, UBound(vVALs, 2) + 2).Resize(UBound(vVALs, 1), UBound(vVALs, 2)) = vVALs
    End With

    GoTo bm_Safe_Exit
bm_Nothing_To_Do:
    MsgBox "There is not enough data in  " & sErrorRng & " to perform expansion." & Chr(10) & _
           "This could be due to a single column of values or one or more blank column(s) of values." & _
            Chr(10) & Chr(10) & "There is nothing to expand.", vbInformation, _
           "Single or No Column of Raw Data"
    GoTo bm_Safe_Exit
bm_Output_Exceeded:
    MsgBox "The number of expanded values created from " & sErrorRng & _
           " (" & Format(iMAXROWS, "\> #, ##0") & " rows × " & UBound(vTMPs, 2) & _
           " columns) exceeds the rows available (" & Format(Rows.Count, "#, ##0") & ") on this worksheet.", vbCritical, _
           "Too Many Entries"
bm_Safe_Exit:
    appTGGL
End Sub

Sub appTGGL(Optional bTGGL As Boolean = True)
    Application.EnableEvents = bTGGL
    Application.ScreenUpdating = bTGGL
End Sub

열 머리글 레이블을 열 A에서 시작하는 행 2에 넣고 데이터를 바로 아래에 놓습니다.

워크시트의 행 수를 초과할 경우 경고하는 오류 컨트롤을 추가했습니다.이는 일반적으로 고려 사항이 될 가능성이 높은 것은 아니지만, 결정되지 않은 수의 열에 있는 값을 서로 곱하면 많은 결과가 빠르게 생성될 수 있습니다.1,048,576 행을 초과하는 것은 예측할 수 없는 일이 아닙니다.

        Variant Array expansion

나열된 테이블의 모든 조합 결과의 데카르트 곱을 반환하는 비조인 선택 SQL 문의 전형적인 예입니다.

SQL 데이터베이스 솔루션

Fruit, 테이블로, SQLite, MySQL 되며 Implicit("Animals, Fruit, Country")을 을 가져올 수 .WHERE및 명시적입니다.JOIN 조인:

SELECT Animals.Animal, Fruits.Fruit, Countries.Country
FROM Animals, Countries, Fruits;

Cartesian SQL

Excel 솔루션

동물, 국가 및 과일 범위가 포함된 워크북에 대한 ODBC 연결을 사용하여 VBA에서 비조인 SQL 문을 실행하는 경우와 동일한 개념입니다.예를 들어, 각 데이터 그룹은 동일한 이름의 워크시트에 있습니다.

Sub CrossJoinQuery()

    Dim conn As Object
    Dim rst As Object
    Dim sConn As String, strSQL As String

    Set conn = CreateObject("ADODB.Connection")
    Set rst = CreateObject("ADODB.Recordset")

    sConn = "Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};" _
               & "DBQ=C:\Path To\Excel\Workbook.xlsx;"
    conn.Open sConn

    strSQL = "SELECT * FROM [Animals$A1:A3], [Fruits$A1:A3], [Countries$A1:A3] "
    rst.Open strSQL, conn

    Range("A1").CopyFromRecordset rst

    rst.Close
    conn.Close

    Set rst = Nothing
    Set conn = Nothing

End Sub

Cartesian SQL in VBA

이 문제에 대한 저의 첫 번째 접근 방식은 @Jeeped가 게시한 것과 유사했습니다.

  1. 입력 열을 배열에 로드하고 각 열의 행 수를 계산합니다.
  2. 배열을 모든 조합으로 채우기
  3. 출력 범위에 어레이 할당

MicroTimer를 사용하여 위 알고리즘의 각 부분에서 걸리는 평균 시간을 계산했습니다.파트 3은 더 큰 입력 데이터에 대해 총 실행 시간의 90%~93%를 차지했습니다.

다음은 워크시트에 데이터를 쓰는 속도를 향상시키기 위한 시도입니다. 상를정습니다했의수▁a를 했습니다.iMinRSize=17 다이 을상 채수있 이상을 수 됩니다.iMinRSize같은 값을 가진 연속된 행은 배열 파일링을 중지하고 워크시트 범위에 직접 씁니다.

Sub CrossJoin(rSrc As Range, rTrg As Range)

  Dim vSrc() As Variant, vTrgPart() As Variant
  Dim iLengths() As Long
  Dim iCCnt As Integer, iRTrgCnt As Long, iRSrcCnt As Long
  Dim i As Integer, j As Long, k As Long, l As Long
  Dim iStep As Long

  Const iMinRSize As Long = 17
  Dim iArrLastC As Integer

  On Error GoTo CleanUp
  Application.ScreenUpdating = False
  Application.EnableEvents = False

  vSrc = rSrc.Value2
  iCCnt = UBound(vSrc, 2)
  iRSrcCnt = UBound(vSrc, 1)
  iRTrgCnt = 1
  iArrLastC = 1
  ReDim iLengths(1 To iCCnt)
  For i = 1 To iCCnt
    j = iRSrcCnt
    While (j > 0) And IsEmpty(vSrc(j, i))
      j = j - 1
    Wend
    iLengths(i) = j
    iRTrgCnt = iRTrgCnt * iLengths(i)
    If (iRTrgCnt < iMinRSize) And (iArrLastC < iCCnt) Then iArrLastC = iArrLastC + 1
  Next i

  If (iRTrgCnt > 0) And (rTrg.row + iRTrgCnt - 1 <= rTrg.Parent.Rows.Count) Then
    ReDim vTrgPart(1 To iRTrgCnt, 1 To iArrLastC)

    iStep = 1
    For i = 1 To iArrLastC
      k = 0
      For j = 1 To iRTrgCnt Step iStep
        k = k + 1
        If k > iLengths(i) Then k = 1
        For l = j To j + iStep - 1
          vTrgPart(l, i) = vSrc(k, i)
        Next l
      Next j
      iStep = iStep * iLengths(i)
    Next i

    rTrg.Resize(iRTrgCnt, iArrLastC) = vTrgPart

    For i = iArrLastC + 1 To iCCnt
      k = 0
      For j = 1 To iRTrgCnt Step iStep
        k = k + 1
        If k > iLengths(i) Then k = 1
        rTrg.Resize(iStep).Offset(j - 1, i - 1).Value2 = vSrc(k, i)
      Next j
      iStep = iStep * iLengths(i)
    Next i
  End If

CleanUp:
  Application.ScreenUpdating = True
  Application.EnableEvents = False
End Sub

Sub test()
  CrossJoin Range("a2:f10"), Range("k2")
End Sub

우리가 설정하면,iMinRSizeRows.Count모든 데이터가 배열에 기록됩니다.결과입니다.

enter image description here

행 수가 가장 많은 입력 열이 먼저 와야 코드가 가장 잘 작동하지만, 열을 순위로 지정하고 올바른 순서로 처리하도록 코드를 수정하는 것은 큰 문제가 되지 않습니다.

워크시트 공식을 사용하여 이 작업을 수행할 수 있습니다.NAME'd 범위(동물, 과일 및 국가)가 있는 경우 "트릭"은 해당 배열에 인덱스를 생성하여 모든 다양한 조합을 제공하는 것입니다.

예:

=CEILING(ROWS($1:1)/(ROWS(Fruits)*ROWS(Countries)),1)

에서는 과일 * 국가의 숫자 항목에 대해 반복되는 1 기반 일련의 숫자를 생성합니다. 이 숫자는 각 동물에 대해 필요한 행 수를 제공합니다.

=MOD(CEILING(ROWS($1:1)/ROWS(Countries),1)-1,ROWS(Fruits))+1

국가 수에 대해 각 과일을 반복하는 1 기반 시리즈를 생성합니다.

=MOD(ROWS($1:1)-1,ROWS(Countries))+1))

1..n의 반복 시퀀스를 생성합니다. 여기서 n은 국가 수입니다.

공식화(일부 오류 확인 포함)

D3:  =IFERROR(INDEX(Animals,CEILING(ROWS($1:1)/(ROWS(Fruits)*ROWS(Countries)),1)),"")
E3:  =IF(E3="","",INDEX(Fruits,MOD(CEILING(ROWS($1:1)/ROWS(Countries),1)-1,ROWS(Fruits))+1))
F3:  =IF(E3="","",INDEX(Countries,MOD(ROWS($1:1)-1,ROWS(Countries))+1))

enter image description here

사실, 저는 제 예전 답변을 수정하고 싶습니다.하지만, 저의 새로운 대답은 예전의 대답과 완전히 다릅니다.오래된 답은 특정 열에 대한 것이고 이 답은 범용 열에 대한 것이기 때문입니다.질문자는 오래된 답변에 대답한 후 보편적으로 하고 싶은 새로운 요구사항을 말합니다.고정 기둥에 대해서는 고정 루핑을 생각할 수 있고 무한 기둥에 대해서는 다른 방법으로 생각할 필요가 있습니다.그래서 저도 합니다.그리고 SO 사용자들도 코드 차이를 볼 수 있고, 이것은 초보자들에게 도움이 될 것이라고 생각합니다.

이 새로운 코드는 이전 코드처럼 그렇게 간단하지 않습니다.코드에 대해 명확하게 알고 싶다면 코드를 한 줄씩 디버그하는 것을 제안했습니다.

코드에 대해서는 걱정하지 마세요.저는 이미 그것에 대해 단계별로 테스트했습니다.저한테 딱 맞습니다.만약 당신이 아니라면, 저에게 알려주세요.한 가지는 이 코드가 빈 행(데이터가 없는 행)에 오류를 일으킬 수 있다는 것입니다.왜냐하면, 현재, 저는 그것에 대한 확인을 추가하지 않았기 때문입니다.

당신의 문제에 대한 나의 보편적인 접근 방식은 다음과 같습니다.

Public Sub matchingCell()

    Dim startRawColumn, endRawColumn, startResultColumn, endResultColumn, startRow As Integer
    Dim index, row, column, containerIndex, tempIndex As Integer
    Dim columnCount, totalCount, timesCount, matchingCount, tempCount As Integer
    Dim isExist As Boolean
    Dim arrayContainer() As Variant

    'Actually, even it is for universal, we need to know start column and end column of raw data.
    'And also start row. And start column for write result.
    'I set them for my test data.
    'You need to modify them(startRawColumn, endRawColumn, startRow, startResultColumn).

    'Set the start column and end column for raw data
    startRawColumn = 1
    endRawColumn = 3

    'Set the start row for read data and write data
    startRow = 2

    'Set the start column for result data
    startResultColumn = 4

    'Get no of raw data column
    columnCount = endRawColumn - startRawColumn

    'Set container index
    containerIndex = 0

    'Re-create array container for count of column
    ReDim arrayContainer(0 To columnCount)

    With Sheets("sheetname")

        'Getting data from sheet

        'Loop all column for getting data of each column
        For column = startRawColumn To endRawColumn Step 1

            'Create tempArray for column
            Dim tempArray() As Variant

            'Reset startRow
            row = startRow

            'Reset index
            index = 0

            'Here is one things. I looped until to blank. 
            'If you want anymore, you can modify the looping type. 
            'Don't do any changes to main body of looping.

            'Loop until the cell is blank
            Do While .Cells(row, column) <> ""

                'Reset isExist flag
                isExist = False

                'Remove checking for no data
                If index > 0 Then

                    'Loop previous data for duplicate checking
                    For tempIndex = 0 To index - 1 Step 1

                        'If found, set true to isExist and stop loop
                        If tempArray(tempIndex) = .Cells(row, column) Then

                            isExist = True

                            Exit For

                        End If

                    Next tempIndex

                End If

                'If there is no duplicate data, store data
                If Not isExist Then

                    'Reset tempArray
                    ReDim Preserve tempArray(index)

                    tempArray(index) = .Cells(row, column)

                    'Increase index
                    index = index + 1

                End If

                'Increase row
                row = row + 1

            Loop

            'Store column with data
            arrayContainer(containerIndex) = tempArray

            'Increase container index
            containerIndex = containerIndex + 1

        Next column

        'Now, we got all data column including data which has no duplicate
        'Show result data on sheet

        'Getting the result row count
        totalCount = 1

        'Get result row count
        For tempIndex = 0 To UBound(arrayContainer) Step 1

            totalCount = totalCount * (UBound(arrayContainer(tempIndex)) + 1)

        Next tempIndex

        'Reset timesCount
        timesCount = 1

        'Get the last column for result
        endResultColumn = startResultColumn + columnCount

        'Loop array container
        For containerIndex = UBound(arrayContainer) To 0 Step -1

            'Getting the counts for looping
            If containerIndex = UBound(arrayContainer) Then

                duplicateCount = 1

                timesCount = totalCount / (UBound(arrayContainer(containerIndex)) + 1)

            Else

                duplicateCount = duplicateCount * (UBound(arrayContainer(containerIndex + 1)) + 1)

                timesCount = timesCount / (UBound(arrayContainer(containerIndex)) + 1)

            End If

            'Reset the start row
            row = startRow

            'Loop timesCount
            For countIndex = 1 To timesCount Step 1

                'Loop data array
                For index = 0 To UBound(arrayContainer(containerIndex)) Step 1

                    'Loop duplicateCount
                    For tempIndex = 1 To duplicateCount Step 1

                        'Write data to cell
                        .Cells(row, endResultColumn) = arrayContainer(containerIndex)(index)

                        'Increase row
                        row = row + 1

                    Next tempIndex

                Next index

            Next countIndex

            'Increase result column index
            endResultColumn = endResultColumn - 1

        Next containerIndex

    End With

End Sub

여기 재귀 버전이 있습니다.코어 함수가 탭으로 구분된 제품 문자열을 반환하기 때문에 데이터에 내부 탭이 없는 것으로 가정합니다.주 서브는 출력 범위의 왼쪽 상단 모서리 셀과 함께 데이터로 구성된 범위를 통과해야 합니다.이것은 아마도 약간 조정될 수 있지만 테스트 목적에 적합합니다.

ColumnProducts Range("A:C"), Range("E1")

OP 문제를 해결하는 통화입니다.코드는 다음과 같습니다.

'the following function takes a collection of arrays of strings
'and returns a variant array of tab-delimited strings which
'comprise the (tab-delimited) cartesian products of
'the arrays in the collection

Function CartesianProduct(ByVal Arrays As Collection) As Variant
    Dim i As Long, j As Long, k As Long, m As Long, n As Long
    Dim head As Variant
    Dim tail As Variant
    Dim product As Variant

    If Arrays.Count = 1 Then
        CartesianProduct = Arrays.Item(1)
        Exit Function
    Else
        head = Arrays.Item(1)
        Arrays.Remove 1
        tail = CartesianProduct(Arrays)
        m = UBound(head)
        n = UBound(tail)
        ReDim product(1 To m * n)
        k = 1
        For i = 1 To m
            For j = 1 To n
                product(k) = head(i) & vbTab & tail(j)
                k = k + 1
            Next j
        Next i
        CartesianProduct = product
    End If
End Function

Sub ColumnProducts(data As Range, output As Range)
    Dim Arrays As New Collection
    Dim strings As Variant, product As Variant
    Dim i As Long, j As Long, n As Long, numRows As Long
    Dim col As Range, cell As Range
    Dim outRange As Range

    numRows = Range("A:A").Rows.Count
    For Each col In data.Columns
        n = col.EntireColumn.Cells(numRows).End(xlUp).Row
        i = col.Cells(1).Row
        ReDim strings(1 To n - i + 1)
        For j = 1 To n - i + 1
            strings(j) = col.Cells(i + j - 1)
        Next j
        Arrays.Add strings
    Next col
    product = CartesianProduct(Arrays)
    n = UBound(product)
    Set outRange = Range(output, output.Offset(n - 1))
    outRange.Value = Application.WorksheetFunction.Transpose(product)
    outRange.TextToColumns Destination:=output, DataType:=xlDelimited, Tab:=True
End Sub

좋아요, 그럼 당신은 가능한 모든 조합의 목록을 원하는 거군요.제가 할 일은 다음과 같습니다.

  • 먼저 원시 데이터를 선택하고 열별로 중복 항목을 제거합니다.
  • 그런 다음 이 세 개의 열을 세 개의 개별 배열로 읽습니다.
  • 모든 배열의 총 길이를 계산합니다.
  • 그런 다음 루프 페이스트를 사용하여 국가 배열의 첫 번째 값을 동물과 과일의 조합 수만큼 반복해서 배열의 길이를 늘렸습니다.
  • 루프 안에서 모든 과일 옵션을 게시하는 또 다른 루프를 만듭니다.최대 동물 수와 동일한 중복 행 수를 사용합니다.
  • 그런 다음 표의 마지막 행까지 서로 따라붙지 않고 동물을 붙여넣습니다.

여기, 당신의 문제에 대한 나의 접근법.

Public Sub matchingCell()

    Dim animalRow, fruitRow, countryRow, checkRow, resultRow As Long
    Dim isExist As Boolean

    'Set the start row
    animalRow = 2
    resultRow = 2

    'Work with data sheet
    With Sheets("sheetname")

        'Loop until animals column is blank
        Do While .Range("A" & animalRow) <> ""

            'Set the start row
            fruitRow = 2

            'Loop until fruits column is blank
            Do While .Range("B" & fruitRow) <> ""

                'Set the start row
                countryRow = 2

                'Loop until country column is blank
                Do While .Range("C" & countryRow) <> ""

                    'Set the start row
                    checkRow = 2

                    'Reset flag
                    isExist = False

                    'Checking for duplicate row
                    'Loop all result row until D is blank
                    Do While .Range("D" & checkRow) <> ""

                        'If duplicate row found
                        If .Range("D" & checkRow) = .Range("A" & animalRow) And _
                           .Range("E" & checkRow) = .Range("B" & fruitRow) And _
                           .Range("F" & checkRow) = .Range("C" & countryRow) Then

                           'Set true for exist flag
                           isExist = True

                        End If

                        checkRow = checkRow + 1

                    Loop

                    'If duplicate row not found
                    If Not isExist Then

                        .Range("D" & resultRow) = .Range("A" & animalRow)
                        .Range("E" & resultRow) = .Range("B" & fruitRow)
                        .Range("F" & resultRow) = .Range("C" & countryRow)

                        'Increase resultRow
                        resultRow = resultRow + 1

                    End If

                    'Increase countryRow
                    countryRow = countryRow + 1

                Loop

                'Increase fruitRow
                fruitRow = fruitRow + 1

            Loop

            'Increase fruitRow
            animalRow = animalRow + 1

        Loop

    End With

End Sub

이미 테스트해 봤어요.잘 작동합니다.좋은 하루 되세요.

먼저 다음과 같이 데이터를 입력해야 합니다.데이터 저장 방법

주파수를 함께 추가할 새 열을 추가합니다.간단한 재귀 공식을 수행합니다.(예: f3+f4)

최신 버전의 Excel과 새로운 기능 Xlookup으로 가져가기 위해, 저는 다음 공식을 제안합니다: =XLOOKUP(ROWS(K$2[a]:K2), $I$3:$I$8[b], $H$3:$H$8[c]"모든 주파수 충족", 1,1

위치:

데이터를 표시할 열입니다.번호를 잠그는 것이 중요합니다.

주파수가 함께 추가된 것입니까?

해당 주파수에서 표시할 요소입니다.

작동 방식:

ROWS(K$2[a]:K2) : 컬럼의 위치를 결정합니다.첫 번째 셀에서는 첫 번째 위치에서 자신을 고려합니다.다음 셀은 두 번째 셀이 될 것입니다.

XLOOKUP 파트: 위치가 확보되면 ROWS()에서 발견된 위치가 첫 번째 주파수보다 낮거나 같은지(왜 첫 번째 주파수를 사용하는지) 비교합니다.

이 경우 해당 주파수와 관련된 요소가 표시됩니다.

첫 번째 주파수보다 크면 두 번째 주파수 등을 확인합니다.

최대 결합 주파수보다 먼 경우 "모든 주파수 충족"으로 표시됩니다.

마지막 1은 이 기능에 필요하지 않습니다.

언급URL : https://stackoverflow.com/questions/31472816/expanding-column-cells-for-each-column-cell