Vimでダミーデータを作る

  • 46
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

そんなプラグインを書きました。この記事はその使い方の紹介です。

ダミーデータというのは、適当な名前とか、いわゆる Lorem ipsum みたいな無意味な文章といった穴埋め用途のテキストのことです。

:echo fake#gen("paragraph")

Ignota autem nostrud albucius sagittis no fabulas erat eam ridens
et per moderatius 98 movet. Sea iure est integre metus adipisci
justo id con ultrices omnes minim odioaccommodare. Consul omnium
enim volumus lectus habeo fuisset pri utinam sem tamquam no.

簡単な使い方

適当なプラグインマネージャーでインストールします。
https://github.com/tkhren/vim-fake

NeoBundle 'tkhren/vim-fake'

生成してみる

fake#gen({keyname}) という関数を呼ぶと、実行ごとに異なる答えが返ってきます。
{keyname}にはいろいろありますが、例えば国の名前が得たいなら、"country" を使います。

:echo fake#gen("country")
Canada
:echo fake#gen("country")
Nigeria
:echo fake#gen("country")
Indonesia

挿入してみる

挿入モード中にダミーデータを生成したいなら、 Expression register を使います。

<C-r>=fake#gen("country")

(適当なスニペットプラグインを使えばもっと簡単に挿入できます。)
参考: vim-fake & neosnippet example - Gist
fakesnip

置換してみる①

次のテキストの dummy を男性の名前(male_name)で置換したいなら、

<ul>
    <li> dummy </li>
    <li> dummy </li>
    <li> dummy </li>
</ul>

:%s/dummy/\=fake#gen("male_name")/g を実行するとdummyがテキトーな人の名前に変わります。

<ul>
    <li> Steve </li>
    <li> Rodney </li>
    <li> Leonard </li>
</ul>

置換してみる②

いちいち置換コマンド打つのは面倒なとき、大量のテキストを生成したい時には、
プレースホルダーを使うのが便利です。

まず、編集中のファイルに FAKE__{keyname}__ を書いておきます。

<user>
    <name>FAKE__fullname__</name>
    <age>FAKE__age__</age>
    <job>FAKE__job__</job>
</user>
<user>
    <name>FAKE__fullname__</name>
    <age>FAKE__age__</age>
    <job>FAKE__job__</job>
</user>

:FakeSubstitute を実行すると、FAKE__{keyname}__ のそれぞれを fake#gen({keyname})で一括置換します。

<user>
    <name>Lydia Dawson</name>
    <age>59</age>
    <job>Dentist</job>
</user>
<user>
    <name>Charlie Adams</name>
    <age>42</age>
    <job>Musician</job>
</user>

データソース(keyname)の選択

ランダムデータの生成には fake#gen({keyname}) を使います。
この {keyname} には使いたいデータの名前を指定します。デフォルトでは次のものが用意されています。

{keyname} 内容 並び順
male_name 世界の男性名 人口順
female_name 世界の女性名 人口順
surname 世界の苗字 人口順
country 国の名前 人口順
gtld gTLD 登録数順
job 仕事の名前  
word 頻出英単語  
nonsense 意味のない単語  

これらのデータソース(辞書ファイル)は、UNIXの/usr/share/dict/wordsのような単語が行ごとに羅列されただけの単純なテキストファイルです。

例えば country 辞書には、世界の国の名前が書いてありますが、fake#gen("country") すればこのファイルの中のランダムな行が返されます。CanadaとかBrazilとかが返ってきます。選択する行は一様にランダムです。

:echo fake#gen("country")
China

辞書ファイルの追加

この辞書ファイルはいくつでも任意に追加することができ、globしてはじめに見つかったファイルを使います。g:fake_src_paths には辞書ファイルの入ったディレクトリを指定します。

let g:fake_src_paths = ['/home/user/.vim/fake_src1',
                        \ '/home/user/.vim/fake_src2']

このソースディレクトリは階層にすることもできるので言語や用途ごとに分けたりできます(fake#gen("ja/male_name")のようにアクセスできる)。

データ生成を定義する

本領はここからです。 辞書ファイルを組み合わせることで、いろいろなデータの生成を定義することができます。

fake#define({keyname}, {code}) という関数を使って新しい{keyname}を定義します。

fake#gen({keyname}){keyname}が定義されていれば、eval({code})を返し、もし定義されていなければ、g:fake_src_pathsに辞書ファイルを探しに行き、そのランダムな行を返します。

どんな組み合わせができるかというと、例えば

"" リストからランダムにひとつ要素を返す
call fake#define('sex', 'fake#choice(["male", "female"])')

"" 男性と女性の名前を得る
call fake#define('name', 'fake#int(1) ? fake#gen("male_name")'
                                \ . ' : fake#gen("female_name")')

"" フルネームを得る
call fake#define('fullname', 'fake#gen("name") . " " . fake#gen("surname")')

"" 世代人口に沿った年齢を返す
call fake#define('age', 'float2nr(floor(110 * fake#betapdf(1.0, 1.45)))')

"" 国の人口割合に応じて国の名前を返す(China, Indiaが頻繁に返る)
call fake#define('country', 'fake#get(fake#load("country"),'
                        \ . 'fake#betapdf(0.2, 4.0))')

"" 登録ウェブサイトの数に応じたgTLDを返す(com, netあたりが頻繁に出る)
call fake#define('gtld', 'fake#get(fake#load("gtld"),'
                        \ . 'fake#betapdf(0.2, 3.0))')

"" メールアドレスを生成
call fake#define('email', 'tolower(printf("%s@%s.%s",'
                        \ . 'fake#gen("name"),'
                        \ . 'fake#gen("surname"),'
                        \ . 'fake#gen("gtld")))')

"" Lorem ipsum みたいな意味不明な文章を生成
call fake#define('sentense', 'fake#capitalize('
                        \ . 'join(map(range(fake#int(3,15)),"fake#gen(\"nonsense\")"))'
                        \ . ' . fake#chars(1,"..............!?"))')

call fake#define('paragraph', 'join(map(range(fake#int(3,10)),"fake#gen(\"sentense\")"))')

"" エイリアスを作るならこんな感じ
call fake#define('lipsum', 'fake#gen("paragraph")')

このようなコードをvimrcあたりに書いておけば、fake#gen("fullname") のような形で呼び出すことができます。上のコードは :let g:fake_bootstrap = 1 を定義すれば勝手に読み込まれ、デフォルトは 0 です。好みに応じてONにしてください。

生成方法に偏りをつける(重み付け乱択)

int()とかcapitalize()などは命名からなんとなく分かるかもしれませんが、それ以外にも幾つか知らない関数が出てきたと思います。

"" 辞書ファイル({dictname})の行のリストを返す(内部的に一時キャッシュされる)
fake#load({dictname})

"" listからランダムな要素を返す。
fake#choice({list})

"" リストの要素を相対位置で取得する。rateは0.0 ~ 1.0
"" 例えば rate=0.0 ならリストの先頭要素を返す, 0.5 ならリストの中央の要素を返す
fake#get({list}, {rate})

"" a,bで与えられるベータ分布を返す(形状パラメーターa,b > 0, 返り値は0.0 ~ 1.0)
fake#betapdf({a}, {b})

choice() は与えられたリストからランダムに要素を取り出します。取り出し方は一様であり、偏りはありません。

重み付けされた要素選択をしたい場合は、get()betapdf()を組み合わせます。get()は第二引数に取り出したい要素のインデックスを相対位置で指定できるため、0.0~1.0を返すbetapdf()を指定すると、リストのどのあたりの要素を頻繁に取り出したいかを指定できます。

betapdf()は 0.0 ~ 1.0 を返すベータ分布の確率密度関数で、一様ではない偏った乱数を得ることができます。パラメーターa,bの取り方は、次の記事を参考にしながら決めてください。

http://www.ntrand.com/jp/beta-distribution/
http://keisan.casio.jp/exec/system/1161228837

実は country 辞書は人口の多い順に国名が並んでいます。なので人口の多いChinaとかIndiaを頻繁に欲しいなら、次のように書けばリストの先頭要素(辞書ファイルの先頭要素)を多く選択するので、それっぽくシュミレーションできます。 ※厳密な人口比率に沿った乱択をするわけではありません。

"" 国の人口割合に応じて国の名前を返す(China, Indiaが頻繁に返る)
call fake#define('country',
    \ 'fake#get(fake#load("country"), fake#betapdf(0.2, 4.0))')

:echo fake#gen("country")
China
:echo fake#gen("country")
India
:echo fake#gen("country")
China

下図 betapdf(0.2, 4.0) のグラフ
87746340248.png

その他

乱数の精度

Vimには組み込みの乱数生成関数が無いので、sha256()を10進数に直して苦し紛れに乱数としています。そのため、MTとかXorShiftと比較すれば一様性は非常に悪いと思います。それでも今回の用途としては十分な品質と考えているので、Vital.vimにXorShiftがありますが結局使いませんでした。

データソースの内容について

デフォルトの辞書ファイルは使い方を説明するためだけにバンドルしているようなもので、内容が充実しているとは言えませんし正確性も保証できません。なので今後勝手に内容更新したりする可能性もあります。内容に不備・不満等があれば、自前で辞書を用意したほうが早いと思います。