PR

Excel VBAでワードリストからランダムデータを生成する方法|候補数が異なる列にも対応

Excel VBA ランダムデータ生成 Excel
スポンサーリンク

はじめに

テスト用データやサンプルデータを作るとき、あらかじめ用意した候補から品番・工程・担当などをランダムに組み合わせたいことがあります。候補数がすべて同じなら単純ですが、列ごとに候補数が異なる表では、空欄まで抽選対象にすると結果に空白が混ざります。

Excel VBAを使えば、列ごとに空欄ではない候補だけを集め、その候補数に合わせてランダムに値を選べます。この記事では、候補数が異なる複数のワードリストから、指定した件数のランダムデータを自動生成する方法を解説します。

今回作るランダムデータ

「候補」シートに次のような表があるものとします。列ごとに候補数が違っていても構いません。

品番工程担当
A-01加工田中
A-02検査佐藤
A-03梱包
A-04

この例では、品番は4件、工程は3件、担当は2件です。候補数が同じ列でも同じコードを使えます。さらに「候補」シートのE2セルへ作成したいデータ件数を入力します。

例えばE2へ「5」と入力してマクロを実行すると、「結果」シートへ次のようなデータを5行作成します。

品番工程担当
A-03検査田中
A-01加工佐藤
A-04梱包佐藤
A-02加工田中
A-03検査佐藤

組み合わせは実行するたびに変わります。同じ候補が複数行で選ばれることもあります。この記事では「候補からランダムに選ぶ」ことを扱い、同じ値を二度使わない抽選は対象にしません。

事前準備

Excelブックに「候補」と「結果」という2つのワークシートを用意します。「候補」シートは1行目を見出し、2行目以降を候補データとします。候補列はA列から右方向へ連続して配置してください。

作成件数は「候補」シートのE2セルへ正の整数で入力します。候補表がA~C列の場合、E列は候補表の外なので、そのまま件数入力に使えます。候補列を増やす場合は、DataCountCellを候補表と重ならないセルへ変更してください。

コードはVisual Basic Editor(VBE)の標準モジュールへ貼り付けます。「結果」シートは出力専用として扱います。マクロ実行時にシート内の既存内容を消去してから結果を書き込みます。残したい内容がある場合は、出力専用シートを別に用意してください。

VBAコード

Option Explicit

Sub CreateRandomData()
    Const CandidateSheetName As String = "候補"
    Const ResultSheetName As String = "結果"
    Const HeaderRow As Long = 1
    Const CandidateStartRow As Long = 2
    Const DataCountCell As String = "E2"
    Const ResultStartCell As String = "A2"

    Dim wsCandidate As Worksheet
    Dim wsResult As Worksheet
    Dim lastColumn As Long
    Dim lastRow As Long
    Dim dataCount As Long
    Dim dataCountValue As Variant
    Dim outputRow As Long
    Dim candidateRow As Long
    Dim col As Long
    Dim randomIndex As Long
    Dim candidates As Collection
    Dim candidateValue As Variant
    Dim headerValue As Variant
    Dim headerData As Variant
    Dim resultData() As Variant

    Set wsCandidate = ThisWorkbook.Worksheets(CandidateSheetName)
    Set wsResult = ThisWorkbook.Worksheets(ResultSheetName)

    lastColumn = wsCandidate.Cells(HeaderRow, wsCandidate.Columns.Count) _
                            .End(xlToLeft).Column

    If wsCandidate.Range(DataCountCell).Column <= lastColumn _
       And wsCandidate.Range(DataCountCell).Row >= CandidateStartRow Then
        MsgBox "作成件数セルが候補表と重なっています。" _
               & vbCrLf & "DataCountCellを候補表の外へ変更してください。", vbExclamation
        Exit Sub
    End If

    dataCountValue = wsCandidate.Range(DataCountCell).Value2

    If IsError(dataCountValue) Then
        MsgBox "作成件数のセルにエラーがあります。", vbExclamation
        Exit Sub
    End If

    If Not IsNumeric(dataCountValue) Then
        MsgBox "作成件数には正の整数を入力してください。", vbExclamation
        Exit Sub
    End If

    If CDbl(dataCountValue) < 1 _
       Or CDbl(dataCountValue) <> Fix(CDbl(dataCountValue)) Then
        MsgBox "作成件数には正の整数を入力してください。", vbExclamation
        Exit Sub
    End If

    If CDbl(dataCountValue) > _
       wsResult.Rows.Count - wsResult.Range(ResultStartCell).Row + 1 Then
        MsgBox "作成件数が結果シートへ出力できる行数を超えています。", vbExclamation
        Exit Sub
    End If

    dataCount = CLng(dataCountValue)
    ReDim resultData(1 To dataCount, 1 To lastColumn)

    Randomize

    For col = 1 To lastColumn
        headerValue = wsCandidate.Cells(HeaderRow, col).Value2

        If IsError(headerValue) Then
            MsgBox "見出しにエラーがあります。列番号: " & col, vbExclamation
            Exit Sub
        End If

        If Len(CStr(headerValue)) = 0 Then
            MsgBox "見出しが空欄の列があります。列番号: " & col, vbExclamation
            Exit Sub
        End If

        lastRow = wsCandidate.Cells(wsCandidate.Rows.Count, col) _
                             .End(xlUp).Row

        Set candidates = New Collection

        If lastRow >= CandidateStartRow Then
            For candidateRow = CandidateStartRow To lastRow
                candidateValue = wsCandidate.Cells(candidateRow, col).Value2

                If IsError(candidateValue) Then
                    MsgBox "候補リストにエラー値があります。" _
                           & vbCrLf & "列: " & CStr(headerValue) _
                           & " / 行: " & candidateRow, vbExclamation
                    Exit Sub
                End If

                If Len(CStr(candidateValue)) > 0 Then
                    candidates.Add candidateValue
                End If
            Next candidateRow
        End If

        If candidates.Count = 0 Then
            MsgBox "候補が1件もない列があります: " _
                   & CStr(headerValue), vbExclamation
            Exit Sub
        End If

        For outputRow = 1 To dataCount
            randomIndex = Int(candidates.Count * Rnd) + 1
            resultData(outputRow, col) = candidates(randomIndex)
        Next outputRow
    Next col

    wsResult.Cells.ClearContents

    headerData = wsCandidate.Range( _
        wsCandidate.Cells(HeaderRow, 1), _
        wsCandidate.Cells(HeaderRow, lastColumn) _
    ).Value2

    wsResult.Cells(1, 1).Resize(1, lastColumn).Value2 = headerData
    wsResult.Range(ResultStartCell) _
            .Resize(dataCount, lastColumn).Value2 = resultData

    MsgBox dataCount & "件のランダムデータを作成しました。", vbInformation
End Sub

コードのポイント

列ごとに候補だけをCollectionへ集める

候補数が異なる表では、表全体の最終行を1つだけ使いません。各列でEnd(xlUp)により最後のデータ行を確認し、2行目から順に読み取ります。

Collectionは、複数の値を順番にまとめて保持できるVBAのオブジェクトです。空欄ではない値だけをCollectionへ追加するため、列ごとに候補数が違っても実在する候補だけを抽選できます。途中の空欄も候補へ追加されません。

RandomizeとRndで候補番号を決める

Randomizeは乱数ジェネレーターを初期化し、Rndは0以上1未満の値を返します。候補数をcandidates.Countで取得し、次の式でCollectionの1番目から最後までの番号を作ります。

randomIndex = Int(candidates.Count * Rnd) + 1

例えば候補が4件なら、randomIndexは1~4のいずれかになります。各出力行・各列でこの処理を行い、選ばれた値を二次元配列resultDataへ格納します。

候補がない列や不正な件数は処理を止める

候補がない列や、空欄・文字列・0以下・小数の作成件数では処理を続けられません。

コードでは作成件数、見出し、候補を確認し、不正な場合はメッセージを表示して終了します。候補セルにExcelのエラー値がある場合も停止します。

結果は配列にためて一括出力する

抽選結果はセルへ1件ずつ書き込まず、resultDataへためてからResizeで確保した範囲へ一括出力します。作成件数と候補列数に合わせて出力範囲が決まるため、候補列を増減しても同じ考え方で処理できます。

実行手順

  1. 「候補」シートの1行目へ見出しを入力します。
  2. 2行目以降へ列ごとの候補を入力します。列ごとに候補数が違っていても構いません。
  3. 「候補」シートのE2へ作成件数を正の整数で入力します。
  4. 「結果」シートを作成します。
  5. 標準モジュールへコードを貼り付けます。
  6. CreateRandomDataを実行します。
  7. 「結果」シートに指定件数のデータが作成されたことを確認します。

候補を追加・削除した後も、同じマクロを再実行すれば現在の候補からデータを作り直せます。

よくある失敗

作成件数セルが候補表と重なっている

この例では作成件数をE2へ入力します。候補列をE列まで増やした場合、E2は候補データと重なります。コードは重なりを検出すると停止するため、DataCountCellを候補表の外にあるセルへ変更してから実行してください。

見出し行の途中に空欄がある

コードは1行目の右端までを候補列として扱います。その途中に見出しがない列があると、どのデータを生成する列なのか判断できないため処理を停止します。候補列はA列から右方向へ連続して配置します。

候補の重複を禁止したい

このコードは各行を独立して抽選するため、同じ候補が複数回選ばれることがあります。「一度選んだ候補は次に使わない」という抽選は、候補を削除しながら選ぶなど別の処理が必要です。この記事の対象は、候補の再利用を許可したランダムデータ生成です。

まとめ

Excel VBAで列ごとに候補数が異なるワードリストを扱う場合は、列ごとに空欄ではない候補だけを集め、その候補数に合わせてランダムな番号を選ぶと安全にデータを生成できます。

この方法なら、候補数をそろえるために表へダミーデータを追加する必要はありません。作成件数、見出し、候補の有無を確認してから抽選し、結果を配列へためて一括出力することで、候補列や作成件数が変わっても使いやすい処理になります。

関連記事


タイトルとURLをコピーしました