Google Apps Scriptでダミーデータをジェネレートする【GAS】

Google Apps Scriptの開発に限らず、業務用アプリケーション等の開発時には、わりと大きなダミーデータが必要になるケースがあります。ネット上にはそれらに向けてのダミーデータ生成サービスがたくさん存在しています。中には数万オーダー規模のデータを一瞬で生成できてしまうサービスも。

実際に開発をするにあたっては、実際の生データを使うのは色々とマズイケースもあるので、これらのデータを使うのですが、必ずしも自分の開発で必要なデータを毎度毎度生成できるとは限りません。そこで、自前でもダミーデータを生成できるような小ツールがあったら便利だなという事で、作ってみました。

色々と自分の開発にあったデータセットを用意しておくと、手頃なダミーデータを素早く用意できます(その他の一般的なデータはダミーデータジェネレータサイトで用意が出来ますから)。

今回使用するファイル

事前準備

今回のダミーデータジェネレータは、5個の辞書から、ID、苗字、名前、メアド、住所というポピュラーな要素をジェネレートする事が可能です。ここにオリジナルの生成用のデータ(自分であれば薬剤のデータ等)を追加すれば、開発用ダミーデータの生成として特化したものを作ることが可能です。

但し、現在スプレッドシートは500万セルまでという上限があるので、辞書データの追加には気を使う必要があります。

今回使用したダミーデータは以下のような形で揃えました。

  1. 苗字および氏名のダミーデータはVectorのMS-IME95用人名辞書データを取り込んでみました。(苗字4000件、名前19,000件)
  2. ひらがな⇒ローマ字変換用の関数としてkana2romajiのライブラリを使って、ローマ字を生成するkanahenkan関数を作成しました。以下がそのコード。メアド生成時に使用します。
  3. JPNICのドメイン一覧を加工して取り込みました。メアド生成時に使用します。(280件)
  4. 適当なドメイン生成の為に、EDICT英単語辞書から単語を抽出して取り込みました。(98,000件)
  5. 住所データは住所.jpで公開している日本の住所データを取り込みました(149,000件)
  6. 各辞書データには名前付き範囲を設定しています。
  7. 辞書データが大きすぎる場合にはファイルを分割して、コードの中で参照するファイルを読み込むようにすればよいかと思います。
  8. 辞書データが多すぎるとデータの生成に時間がかかりますので、ほどほどに。特に住所データ。

図:各データには名前付き範囲を設定しています。

//指定範囲の値を配列で取得してかな→英字変換して返す
function kanahenkan(){
  //返り値用の配列
  var array = [];
  
  //rangeを指定
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("姓名");
  var range = sheet.getRange("B2:B4061").getValues();
  
  //配列データを変換してpush
  var length = range.length;
  var kanaword = "";
  var henkan = "";

  for(var i = 0;i<length;i++){
    //かなを取得する
    kanaword = range[i][0];

    //変換ワードを取得
    henkan = kana2romaji(kanaword);
    
    //配列にpushする
    array.push([henkan]);
  }

  //配列の縦横の数を取得してシートに貼り付け
  var lastColumn = array[0].length;  //カラムの数を取得する
  var lastRow = array.length;      //行の数を取得する
  sheet.getRange(2,3,lastRow,1).setValues(array);

}

//https://github.com/cloneko/kana2romaji
function kana2romaji(string) {

    var arrayedString = string;
    var value = '';

    var triTable = {
        'う゛ぁ': 'va','う゛ぃ' : 'vi', 'う゛ぅ' : 'vu','う゛ぇ' : 've', 'う゛ぉ' : 'vo'
    };

    var biTable = {
        'きゃ' : 'kya','きゅ' : 'kyu','きょ' : 'kyo',
        'ぎゃ' : 'gya','ぎゅ' : 'gyu','ぎょ' : 'gyo',
        'しゃ' : 'sha','しゅ' : 'shu','しょ' : 'sho',
        'じゃ' : 'ja','じゅ' : 'ju','じょ' : 'jo',
        'ちゃ' : 'cha','ちゅ' : 'chu','ちょ' : 'cho',
        'ぢゃ' : 'dya','ぢゅ' : 'dyu','ぢょ' : 'dyo',
        'でゃ' : 'dha','でゅ' : 'dhu','でょ' : 'dho',
        'にゃ' : 'nya','にゅ' : 'nyu','にょ' : 'nyo',
        'ひゃ' : 'hya','ひゅ' : 'hyu','ひょ' : 'hyo',
        'びゃ' : 'pya','びゅ' : 'byu','びょ' : 'byo',
        'みゃ' : 'mya','みゅ' : 'myu','みょ' : 'myo',
        'りゃ' : 'rya','りゅ' : 'ryu','りょ' : 'ryo',
        'てぁ' : 'tha','てぃ' : 'thi','てぇ' : 'tee',
        'う゛' : 'vu','あ゛' : 'a"',
        'っか' : 'kka','っき' : 'kki','っく' : 'kku','っけ' : 'kke','っこ' : 'kko',
        'っさ' : 'ssa','っし' : 'sshi','っす' : 'ssu','っせ' : 'sse','っそ' : 'sso',
        'った' : 'tta','っち' : 'cchi','っつ' : 'ttu','って' : 'tte','っと' : 'tto',
        'っな' : 'nna','っに' : 'nni','っぬ' : 'nnu','っね' : 'nne','っの' : 'nno',
        'っは' : 'hha','っひ' : 'hhi','っふ' : 'ffu','っへ' : 'hhe','っほ' : 'hho',
        'っま' : 'mma','っみ' : 'mmi','っむ' : 'mmu','っめ' : 'mme','っも' : 'mmo',
        'っや' : 'yya', 'っゆ' : 'yyu','っよ' : 'yyo',
        'っら' : 'rra','っり' : 'rri','っる' : 'rru','っれ' : 'rre','っろ' : 'rro',
        'っわ' : 'wwa',
        'っが' : 'gga','っぎ' : 'ggi','っぐ' : 'ggu','っげ' : 'gge','っご' : 'ggo',
        'っざ' : 'zza','っじ' : 'jji','っず' : 'zzu','っぜ' : 'zze','っぞ' : 'zzo',
        'っだ' : 'dda','っぢ' : 'ddi','っづ' : 'ddu','っで' : 'dde','っど' : 'ddo',
        'っば' : 'bba','っび' : 'bbi','っぶ' : 'bbu','っべ' : 'bbe','っぼ' : 'bbo',
        'っぱ' : 'ppa','っぴ' : 'ppi','っぷ' : 'ppu','っぺ' : 'ppe','っぽ' : 'ppo'
        
    };

    var uniTable = {
        'あ' : 'a','い' : 'i','う' : 'u','え' : 'e','お' : 'o',
        'か' : 'ka','き' : 'ki','く' : 'ku','け' : 'ke','こ' : 'ko',
        'さ' : 'sa','し' : 'shi','す' : 'su','せ' : 'se','そ' : 'so',
        'た' : 'ta','ち' : 'chi','つ' : 'tsu','て' : 'te','と' : 'to',
        'な' : 'na','に' : 'ni','ぬ' : 'nu','ね' : 'ne','の' : 'no',
        'は' : 'ha','ひ' : 'hi','ふ' : 'fu','へ' : 'he','ほ' : 'ho',
        'ま' : 'ma','み' : 'mi','む' : 'mu','め' : 'me','も' : 'mo',
        'や' : 'ya','ゆ' : 'yu','よ' : 'yo',
        'ら' : 'ra','り' : 'ri','る' : 'ru','れ' : 're','ろ' : 'ro',
        'わ' : 'wa','を' : 'wo','ん' : 'n',
        'が' : 'ga','ぎ' : 'gi','ぐ' : 'gu','げ' : 'ge','ご' : 'go',
        'ざ' : 'za','じ' : 'ji','ず' : 'zu','ぜ' : 'ze','ぞ' : 'zo',
        'だ' : 'da','ぢ' : 'di','づ' : 'du','で' : 'de','ど' : 'do',
        'ば' : 'ba','び' : 'bi','ぶ' : 'bu','べ' : 'be','ぼ' : 'bo',
        'ぱ' : 'pa','ぴ' : 'pi','ぷ' : 'pu','ぺ' : 'pe','ぽ' : 'po',
        'ぁ' : 'xa','ぃ' : 'xi','ぅ' : 'xu','ぇ' : 'xe','ぉ' : 'xo',
        'ゃ' : 'xya','ゅ' : 'xyu','ょ' : 'xyo','っ' : 'xtsu'
    };

    if(triTable[string] !== undefined){
        return triTable[string];
    } else if(biTable[string] !== undefined) {
        return biTable[string];
    }

    var biCheck = new Object();
    for (var k in biTable){
        var tmp = k.split('');
        biCheck[tmp[0]] = true;
    }

    var triCheck = new Object();
    for (var tk in triTable){
        var tmp = tk.split('');
        triCheck[tmp[0] + tmp[1]] = true;
        biCheck[tmp[0]] = true; 
    }
    

    var buf = '';
    for(var i = 0; i < arrayedString.length ; i++){
        var str = arrayedString[i];
        buf += str;
        if(buf.length == 3){
            if(triTable[buf] !== undefined){
                value += triTable[buf];
            } else {
                tmp = buf.split('');
                value += biTable[tmp[0] + tmp[1]];
                value += uniTable[tmp[2]] === undefined ? tmp[2] : uniTable[tmp[2]];
                
            }

        } else if(buf.length == 2) {
            if(triCheck[buf] !== undefined) { 
            } else if(biTable[buf] !== undefined) {
                    value += biTable[buf];
                    buf = '';
                } else {
                    tmp = buf.split('');
                    value += uniTable[tmp[0]]; 
                    value += uniTable[tmp[1]] === undefined ? tmp[1] : uniTable[tmp[1]]; 
                    buf = '';
                } 
        } else if(biCheck[buf] !== undefined){
        } else { 
                value += uniTable[str] === undefined ? str : uniTable[str];
                buf = '';
        }
        }
        value += buf !== '' ? uniTable[buf] : '';

    value = value.replace(/([aiueo])ー/gi,'$1');
    return value;
}

実行結果とコード

実行と結果

データ整備したらメニューの「▶道具箱」⇒ダミーデータ生成を実行。生成件数を聞いてくるので、数値を入力(今回は最大10000件)。住所データが重すぎる為、100件でも10000件でも対して時間の差がなく時間が掛かります。

図:専門分野のデータを用いて生成が本来の目的(売上データなどなど)

ソースコード

//ダミーデータジェネレーター
function dummyman() {
  //件数を指定するプロンプトを表示する
  var ui = SpreadsheetApp.getUi();
  var ret = ui.prompt("生成する件数を入力",ui.ButtonSet.OK_CANCEL);
  var str = 0;
  
  //押されたボタンによって処理を分岐
  switch(ret.getSelectedButton()){
    case ui.Button.OK:  //OKボタンを押した時の処理
      //入力値を取得
      str = ret.getResponseText();
      
      //数値のみであるか判定する
      if(isFinite(str)){
        //数値なので処理を続行する
      }else{
        //数値じゃないので処理を中断
        ui.alert("数値じゃない値が入っていますよ");
        return;
      }
      break;
    case ui.Button.CANCEL:  //キャンセルを押した時の処理
      ui.alert("キャンセルされました。");
      break;
      
    case ui.Button.CLOSE:
      break;
  }
  
  //ストアされた値が10000以上の場合強制停止
  if(Number(str) >= 10000){
    ui.alert("件数が1万件を超えています。もうちょっと少なくしてください");
    return;
  }
  
  //ダミーデータ生成用辞書の読み込み
  var array = [];
  var sheet = SpreadsheetApp.getActiveSpreadsheet();
  var myoji = sheet.getRangeByName("myouji").getValues();
  var names = sheet.getRangeByName("simei").getValues();
  var domain = sheet.getRangeByName("domain").getValues();
  var word = sheet.getRangeByName("wordman").getValues();
  var address = sheet.getRangeByName("address").getValues();
  
  //各辞書の件数を取得する
  var mlen = myoji.length;
  var nlen = names.length;
  var dlen = domain.length;
  var wlen = word.length;
  var alen = address.length;
  
  //ループでランダムにデータをチョイスしてデータを生成する
  var random = 0;
  for(var i = 0;i<Number(str);i++){
    //一時配列を用意する
    var temparray = [];
    var firstname = "";
    var lastname = "";
    var tempname = "";
    var tempdomain = "";
    var tempaddress = "";
    var banchi = 0;
    var gou = 0;
    
    //IDを生成する
    temparray.push(i + 1);
    
    //苗字を生成する
    random = Math.floor(Math.random() * (mlen - 1)) + 1;
    temparray.push(myoji[random][1]);
    firstname = myoji[random][3];
    
    //氏名を生成する
    random = Math.floor(Math.random() * (nlen - 1)) + 1;
    temparray.push(names[random][1]);
    lastname = names[random][3];
    
    //メールアドレスを生成する
    random = Math.floor(Math.random() * (wlen - 1)) + 1;
    tempname = firstname + "." + lastname + "@" + word[random][3] + ".";
    
    random = Math.floor(Math.random() * (dlen - 1)) + 1;
    temparray.push(tempname + domain[random][0]);
    
    //住所を生成する
    random = Math.floor(Math.random() * (alen - 1)) + 1;
    banchi = Math.floor(Math.random() * 30) + 1;
    gou = Math.floor(Math.random() * 30) + 1;
    tempaddress = address[random][2] + address[random][4] + address[random][6] + address[random][8] + address[random][9] + banchi + "-" + gou;
    
    temparray.push(tempaddress);

    //配列に一時配列の値を格納する
    array.push(temparray);
    
  }
  
  //スプレッドシートの指定範囲をクリア
  var ss =  sheet.getSheetByName("生成").getRange("A2:E");
  ss.clearContent();
  
  //配列データを最終行に一発書込
  var lastColumn = array[0].length;        //カラムの数を取得する
  var lastRow = array.length;            //行の数を取得する
  sheet.getSheetByName("生成").getRange(2,1,lastRow,lastColumn).setValues(array);
  
  //終了処理
  ui.alert("データの生成が完了しました。");

}
  • promptで受け取った数値を数値かどうか判定させ、今回は生成件数の指定は、最大10000件までに制限を加えました。
  • ダミーデータの取得とその件数を取得させています。件数はランダムな数値生成時の最大値として利用します。
  • IDは連番で振っています。
  • 指定範囲内で整数値をランダム生成するには、Math.floor(Math.random() * (最大値 - 1)) + 1;とします。最大値にはそれぞれの配列の件数を利用しています。lengthの最大値はレコード数を示しますが、配列は0から始まるので、本来この最大値は計算時に含まれないのですが、これで問題なく配列の全てをヒットできるはず。
  • メアドは、苗字氏名のローマ字 + 英単語 + ドメインで結合して生成しています。
  • 住所には住所データに番地と号をそれぞれランダム生成して結合しています。丁目は住所データのフィールドにある字丁目を利用しています。
  • 生成した配列データはもちろん、一発書き込みです。毎回実行時にデータはクリアされる仕様です。

関連リンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)