0

私はこのような値のExcelシートを持っています:

R1:A 1 0 1 1 0 1
R2:B 0 0 1 1 0 0
R3:C 1 0 1 1 0 1
R4:D 1 0 1 1 0 1
R5:E 0 0 1 1 00R

出力:
A、C、D(一致する列があるため)
B、E

一致する値に基づいて列をグループ化するVBAスクリプトが必要です。これを大量のデータセット(たとえば417列)とn行で実行する必要があります。もっと一般化してください。手伝ってください。よろしくお願いします。

4

1 に答える 1

2

クリスは絶対に正しいです。「私のすべての問題を解決してください」のような質問は、ここでは受け入れられません。

あなたは VBA をほとんど、またはまったく知らず、この問題をどこから始めればよいか分からないと思います。お気に入りの検索エンジンに「Excel VBA チュートリアル」と入力すると、チュートリアルの選択肢が表示されます。いくつか試してみて、一番気に入ったものを選び、体系的に取り組んでください。驚くほど早く理解を深めることができます。

ただし、最初に、問題に関するチュートリアルを作成します。個々のステートメントについては、VB ヘルプや検索エンジンで簡単に調べることができるので、あまり説明しません。たとえば、最初のステートメントはOption Explicit. 検索エンジンに「excel vba option explicit」と入力すると、このステートメントの機能と、それを含めることをお勧めする理由を説明するページが表示されます。

Excel の開き方、VB エディターの開き方、モジュールの作成方法、マクロの実行方法を知っていることを前提としています。そうでない場合、これらはインターネット上のチュートリアルで最初に説明されることになります.

ワークシートInputでワークブックを作成しました。入力に次のデータをロードしました。

サンプルデータ

行 2 から 6 はデータと一致します。ヘッダー行と、長さの異なるいくつかのデータ行を追加しました。あなたは一般化された解決策を求めていますが、どのように一般化されているかわかりません。これは、あなたが求めているよりも多かれ少なかれかもしれません. 同様のワークシートを作成するか、要件に合わせて以下のコードを変更してください。

次のマクロTest1を VB モジュールにコピーして実行します。

Option Explicit
Sub Test1()

  Dim ColMax As Long
  Dim RowMax As Long

  With Worksheets("Input")

    ' There are many different ways of identifying the last used row and
    ' column.  SpecialCells has a selection of parameters and is worth
    ' knowing so I have decided to use it to identify the last row and
    ' column.

    ColMax = .Cells.SpecialCells(xlCellTypeLastCell).Column
    RowMax = .Cells.SpecialCells(xlCellTypeLastCell).Row

    ' Debug.Print outputs values to the Immediate Window which will be at the
    ' bottom of the VB Editor window.  If the Immediate Window is is missing,
    ' click Ctrl+G.

    Debug.Print "Last used column " & ColMax
    Debug.Print "Last used row " & RowMax

  End With

End Sub

私のデータでは、マクロは次をイミディエイト ウィンドウに出力します。

Last used column 10
Last used row 13

列「J」は10列目です。このコードは、最後に使用された行と列を識別し、マクロが正しい数の行と列を調べるかどうかを知る必要があります。イミディエイト ウィンドウに値を出力すると、コードを簡単にチェックできます。

次に、マクロTest2をモジュールに追加して実行します。

Sub Test2()

  Dim ColCrnt As Long
  Dim ColMax As Long

  With Worksheets("Input")

    ColMax = .Cells.SpecialCells(xlCellTypeLastCell).Column

    Debug.Print "Row 1:"
    For ColCrnt = 1 To ColMax
      Debug.Print "Col " & ColCrnt & "=" & .Cells(1, ColCrnt).Value & "  ";
      If ColCrnt Mod 5 = 0 Then
        Debug.Print
      End If
    Next

  End With

End Sub

私のデータでは、マクロは次をイミディエイト ウィンドウに出力します。

Row 1:
Col 1=Id  Col 2=Value 1  Col 3=Value 2  Col 4=Value 3  Col 5=Value 4  
Col 6=Value 5  Col 7=Value 6  Col 8=Value 7  Col 9=Value 8  Col 10=Value 9  

VB ヘルプまたはインターネットを使用して、私が使用したステートメントの説明を取得することを期待していますが、私が何をしているのかについての説明が必要です。

検討:

ColMax = Worksheets("Input").Cells.SpecialCells(xlCellTypeLastCell).Column

マクロTest2ではWorksheets("Input")、文字列の残りの部分をWith Statement. これにより、コードはより速く、より明確に、より小さくなりますが、ここのように単一の文字列を書くこともできました。

Worksheets("Input")ワークシート全体を参照します。

Worksheets("Input").Xワークシートのパート X を参照します。グラフやデフォルト値を参照することもできましたが、セルを参照したかったのです。 Worksheets("Input").Cellsワークシート内のすべてのセルを参照します。

Worksheets("Input").Cells.Xセルの一部、またはセルを操作するメソッドを参照します。 Worksheets("Input").Cells.Sortたとえば、ワークシートを並べ替えることができます。

Worksheets("Input").Cells.SpecialCellsに関する情報を返す一連のメソッドの 1 つにアクセスできますWorksheets("Input").Cells。与えるパラメーターの追加:Worksheets("Input").Cells.SpecialCells(xlCellTypeLastCell)必要なメソッドを示します。

最後に、.Column必要なプロパティを特定するために追加します。

VBA やほとんどすべての最新のプログラミング言語を理解するには、このドット表記法を理解することが不可欠です。ではX.Y、Y は X の一部、X に適用されるメソッド、または X のプロパティにすることができます。

Worksheets("Input").Cells(R, C)行 R と列 C の単一セルにアクセスできます。R は最小値 1 の整数で、最大値は使用している Excel のバージョンによって異なります。 Rows.Count使用しているバージョンの最大行数を示します。C は、整数 (5 など) または列コード (「E」など) にすることができます。列「A」は列 1 です。

Debug.Print ExpressionExpressionをイミディエイト ウィンドウに 出力し、その後に改行を続けます。Expressionをイミディエイト ウィンドウにDebug.Print Expression;出力しますが、その後に改行を追加しません。

ColCrnt Mod 5ColCrnt の余りを 5 で割った値を返します。この余りが 0 であることをテストすることで、5 行ごとに改行を追加できます。

for ループを使用して、行 1 のすべての値を出力しました。

マクロTest2には 14 個のステートメントしか含まれていませんが、多くの VBA 概念が使用されています。ゆっくりと取り組んでください。F8 キーを使用してマクロ ステートメントを 1 つずつ実行し、各ステートメントの機能を調べます。このマクロを理解できれば、問題を解決するために知っておく必要があるほとんどすべてのことを知っていることになります。

ここで、一致する行について考える必要があります。より複雑な VBA が必要になるため、行の照合に効率的なアルゴリズムは使用しません。知識が蓄積されたら、後でコードを強化できます。私が使用するアプローチは次のとおりです。

  • 行 2 を行 3、4、5、6、... と比較して一致を記録し、前の行と一致した行を記録します。
  • 行 3 を行 6 と比較しますが、行 2 に対して既に一致しているため、4 と 5 に対しては比較しません。

一致を記録するには、行 2、4、および 5 が同じであることを記録する何らかの方法が必要ですが、さらに行 3、6、および 8 が同じであることを発見します。行 4 と行 2 を一致させたので、行 4 と行 5 をチェックしたくありません。

ブール配列を使用して 2 番目の要件を満たします。

Dim Matched() As Boolean

ReDim Matched(2 To RowMax)

For RowMast = 2 to RowMax
  Matched(RowMast) = False
Next

ではDim Matched() As Boolean()動的配列が必要だと言っています。動的配列は、実行時に上限と下限を変更できる配列です。VBA は、動的配列を使用できる数少ない言語の 1 つであり、下限を設定できる数少ない言語の 1 つです。

ReDim Matched(2 To RowMax)下限を 2 (= 最初のデータ行) として指定し、上限を RowMax (= 最後のデータ行) として指定します。ReDim Matched(N)which が N 個のエントリが必要であり、使用されている場合は Option Base ステートメントに従って下限を決定するようにコンパイラに任せるなどのステートメントをよく目にします。Option Base ステートメントを追加または変更することによって誰かが私の配列に干渉したくないので、私は常に下限を指定します。

以下は、Matched のすべての要素を False に設定します。最近のほとんどの言語は変数を初期化するため、これは必要ありません。そうではなかったときのことを覚えており、明示することを好みます。

For RowMast = 2 to RowMax
  Matched(RowMast) = False
Next

P > N > M の場合、行 N と行 P を行 M と照合するとき、Matched(N) と Matched(P) を True に設定して、行 N を後の行に対してテストしないようにします。

試合を記録する方法はたくさんあります。文字列を構築するという大雑把なテクニックを使用します。

マクロTest3は、シークの出力を作成します。これは効率的なコードではありませんが、最小限の VBA で機能します。このマクロをモジュールに追加して実行します。イミディエイト ウィンドウへの出力は、追加した余分な行を除いて、要求した出力です。

A, C, D
B, E, G
F, J
I, L

VBAプログラミングで頑張ってください。

Sub Test3()

  Dim ColCrnt As Long
  Dim ColMax As Long
  Dim MatchCrnt As Boolean
  Dim Matched() As Boolean
  Dim MatchStgTotal As String
  Dim MatchStgCrnt As String
  Dim RowMast As Long    ' The master row; the row I am comparing
                         ' against later rows
  Dim RowMax As Long
  Dim RowSub As Long     ' The subordinate row; the row I am comparing
                         ' against an earlier row

  With Worksheets("Input")

    ColMax = .Cells.SpecialCells(xlCellTypeLastCell).Column
    RowMax = .Cells.SpecialCells(xlCellTypeLastCell).Row

    MatchStgTotal = ""      ' No matches discovered yet

    ' Initialise Matched
    ReDim Matched(2 To RowMax)
    For RowMast = 2 To RowMax
      Matched(RowMast) = False
    Next

    For RowMast = 2 To RowMax
      If Not Matched(RowMast) Then
        ' This row has not been matched against an earlier row

        MatchStgCrnt = ""   ' No matches for row RowMast discovered yet

        For RowSub = RowMast + 1 To RowMax
          ' Match row RowMast against every later row

          If Not Matched(RowSub) Then
            ' This row has not been matched against an earlier row

            MatchCrnt = True     ' Assume RowSub matches RowMast
                                 ' until find otherewise

            For ColCrnt = ColMax To 2 Step -1
              ' Compare cells from right to left so rows with different
              ' numbers of values fails to match quickly.  This is the only
              ' consession to efficiency in this loop.  There are much better
              ' ways of doing this but I think I have included enough VBA in
              ' this tutorial.

              If .Cells(RowMast, ColCrnt).Value <> _
                 .Cells(RowSub, ColCrnt).Value Then
                ' These rows do not match
                MatchCrnt = False
                Exit For   ' N point checking further cells
              End If

            Next

            If MatchCrnt Then
              ' Row RowSub matches RowMast

              ' Add this row's Id to the list of matches against RowMast
              MatchStgCrnt = MatchStgCrnt & ", " & .Cells(RowSub, 1).Value
              Matched(RowSub) = True  ' Do not check this row again

            End If
          End If

        Next RowSub

        If MatchStgCrnt <> "" Then
          ' RowMast has been matched against one or more other rows.
          ' MatchCrnt contains a list of those other rows.
          If MatchStgTotal <> "" Then
            ' A previous row have been matched.
            ' Terminate it's string with a newline
            MatchStgTotal = MatchStgTotal & vbLf
          End If
          MatchStgTotal = _
                     MatchStgTotal & .Cells(RowMast, 1).Value & MatchStgCrnt
        End If
      End If
      ' Note: Matched(RowMast) has not been set if row RowMast has been matched
      '       because I will never loook as row RowMast again.
    Next RowMast

  End With

  Debug.Print MatchStgTotal

End Sub
于 2012-09-24T11:59:29.457 に答える