名前を自動生成する

名前を自動生成する

名前を自動生成する

 pandas の実践編です。苗字と名前をランダムに組み合わせて、氏名を自動生成 する name_generator() 関数 を作成してみます。

氏名データの保存

 最初に NumPy と pandas をインポートしておきます。

# PYTHON_NAME-1

# NumPyとpandasをインポート
import numpy as np
import pandas as pd

 このサイトの name_s.csv というファイルを読み込んで、データフレームに格納します。

# PYTHON_NAME-2

# ファイルのURL
u = "https://python.atelierkobato.com/wp-content/uploads/2019/08/name_s.csv"

# ファイルをデータフレームとして読み込む
data = pd.read_csv(u, engine="python")

 一般に日本語文字列を読み込む場合は encoding = "shift-jis" を指定しますが、氏名には特殊な文字が使われることがあるので、通常のエンコードでは失敗するケースがあります。しかし、engine に python を指定することで、ほとんどの問題は解決します。このような引数は numpy.loadtxt() には存在しないので、敢えて pandas のデータフレームとして読み込んでいるのです。

 name_s.csv の中身を確認しておきましょう。

data

 [Python] 氏名自動生成 name_generator()

 一番左側の列は自動的に割り振られたインデックスです。
 2 列目と 3 列目は苗字とふりがなです。4 列目と 5 列目は男性の名前とふりがな、6 列目と 7 列目は女性の名前とふりがなです。

 苗字、名前 (男)、名前 (女) はそれぞれデータの個数が異なるので、空白部分は NaN で埋められています。

 次は読み込んだデータをローカルディスクに保存します。

# PYTHON_NAME-3

# name_sample.csv をフォルダに保存
data.to_csv("name_list")

 この作業は必須ではないかもしれません。しかし、万が一にもこのサイトから name_s.csv ファイルが削除されてしまうような状況 (?) に備えて、やはり自身の PC にデータを保管しておいたほうがよいでしょう。

 ファイルは Jupyter Notebook がインストールされているフォルダに保存されるはずです。他の場所に保存したい場合は to_csv()メソッドの引数にフォルダのパスを指定してください。ただし、その場合は再度データを読み込む場合にもパスの指定が必要となります。

 少し面倒ですが、フォルダから再度データを読み込みます。
 今度は自動的に割り振られたインデックスの列が含まれているので、index_col に 0 を指定しておきます。

# PYTHON_NAME-4

# フォルダからデータを読み込んで変数 data を上書き
# 左端の列を行ラベルとして設定
data = pd.read_csv("name_list", index_col=0)

 

苗字と名前をランダムに組み合わせる

 苗字と、男性名または女性名をランダムに組み合わせて、氏名を生成してみましょう。

 DataFrame.sample() は行ごと抜き出してしまうので、この場面では少し使いにくいメソッドです。

 numpy.random.choice() を使えば乱数の範囲設定は不要です。
 しかし、苗字とふりがな 1、名前 (男) とふりがな 2、名前 (女) とふりがな 3 は、それぞれセットで抜き出す必要があります。numpy.random.choice() は一次元配列を引数に受け取る関数なので、やはりこの場面で使うことは難しそうです。

 結局、乱数を生成して、苗字と名前にランダムアクセスするというオーソドックスな方法が一番よさそうです。発生乱数の範囲を決定するために列ごとのデータ数が必要です。Series.count() を使うと、特定の列について NaN を除いたデータ数を取得できます (DataFrame に列ラベルでアクセスすると Series を取り出すことになるので、 Seriesオブジェクトの countメソッドを使うことになりますが、同じメソッドが DataFrame にも備わっています)。

# PYTHON_NAME-5

# 苗字(surname)の個数
ct_s = data["苗字"].count()

# 男性(male)の名前の個数
ct_m = data["名前(男)"].count()

# 女性(female)の名前の個数
ct_f = data["名前(女)"].count()

print(ct_s, ct_m, ct_f)
20 10 12

 たとえば、男性の氏名をランダム生成するには、次のようなコードを記述します。

# PYTHON_NAME-6
# 乱数シードを設定
np.random.seed(10)

# 乱数を生成
rd_s = np.random.randint(0, ct_s)
rd_m = np.random.randint(0, ct_m)

# 氏名を作成
name = data.loc[rd_s, "苗字"]\
       + " " + data.loc[rd_m, "名前(男)"]

# ふりがな(phonetic)を作成
ph = data.loc[rd_s, "ふりがな1"]\
     + " " + data.loc[rd_m, "ふりがな2"]

# 氏名とふりがなをタプルにまとめる
name = (name, ph)

print(name)
('町田 剛志', 'まちだ つよし')

 コードNG-6 は、データフレームの loc属性を使ってアクセスする、いわゆる pandas スタイルのコードです。わかりやすい反面、少し冗長に感じるかもしれません。

 配列にアクセスして NumPy スタイルで書いた方が、コードは短くなります。

# PYTHON_NAME-7

# 乱数シードを設定
np.random.seed(10)

# 乱数を生成
rd_s = np.random.randint(0, ct_s)
rd_m = np.random.randint(0, ct_m)

# データフレームの配列にアクセス
dv = data.values

# 氏名とふりがなを作成
name = dv[rd_s, 0] + " " + dv[rd_m, 2]
ph = dv[rd_s, 1] + " " + dv[rd_m, 3]

# 氏名とふりがなをタプルにまとめる
name = (name, ph)

print(name)
('町田 剛志', 'まちだ つよし')

 ただし、列ラベルを使わないので、どの列からデータを抜き出しているのかわかりにくくなります。どちらのスタイルを採用するかは好みで決めてください。上のコードでは、皆さんと実行結果を一致させるために乱数シードを固定していますが、np.randam.seed(10) の行を削除すると、実行するたびに異なる氏名が生成されます。
 

ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装

中古価格
¥2,818から
(2019/7/30 20:24時点)

name_generator()

 最後に氏名を自動生成する関数 name_generator() のコードを載せておきます。

# PYTHON_NAME-8

# 氏名自動生成関数
def name_generator():
    
    # 苗字(surname)の個数
    ct_s = data["苗字"].count()
    
    # 男性(male)の名前の個数
    ct_m = data["名前(男)"].count()

    # 女性(female)の名前の個数
    ct_f = data["名前(女)"].count()
    
    # 性別をランダムに選択
    sex = np.random.choice(["f", "m"])
    
    # 乱数を生成("苗字"の行番号)
    rd_s = np.random.randint(0, ct_s)
    
    if sex == "m":
        k = 2
        sex_label = "男"
        rd_n = np.random.randint(0, ct_m)
    else:
        k = 4
        sex_label = "女"
        rd_n = np.random.randint(0, ct_f)

    # データフレームの配列にアクセス
    dv = data.values

    # 氏名とふりがなを作成
    name = dv[rd_s, 0] + " " + dv[rd_n, k]
    ph = dv[rd_s, 1] + " " + dv[rd_n, k + 1]

    # 関数の戻り値
    return (name, ph, sex_label)

 この関数を実行すると、ランダムに生成された氏名とふりがな、性別が返ります:

np.random.seed(0)

for i in range(10):
    name = name_generator()
    print(name)
('大久保 絵里子', 'おおくぼ えりこ', '女')
('小林 ゆかり', 'こばやし ゆかり', '女')
('加藤 雅人', 'かとう まさと', '男')
('杉田 剛志', 'すぎた つよし', '男')
('山田 直樹', 'やまだ なおき', '男')
('長谷川 彩夏', 'はせがわ あやか', '女')
('山田 潤一郎', 'やまだ じゅんいちろう', '男')
('武田 直樹', 'たけだ なおき', '男')
('中村 雅人', 'なかむら まさと', '男')
('高橋 雅人', 'たかはし まさと', '男')