NGender:
根据中文姓名猜测其性别
- 不到20行纯Python代码(核心部分)
- 无任何依赖库
- 兼容python3, python2, pypy
- 82%的准确率
- 可用于猜测性别
- 也可用于判断名字的男性化/女性化程度
使用
- 安装
- pip install ngender
- 然后在命令行中
$ cli 赵本山 宋丹丹
name: 赵本山 => gender: male, probability: 0.9836229687547046
name: 宋丹丹 => gender: female, probability: 0.9759486128949907
当然也可以在Python程序中用
>>> import ngender
>>> ngender.guess('赵本山')
('male', 0.9836229687547046)
>>> ngender.guess('宋丹丹')
('female', 0.9759486128949907)
>>> %timeit guess('宋丹丹')
100000 loops, best of 3: 4.01 µs per loop
原理(重要)
数学(朴素贝叶斯)
贝叶斯公式: P(Y|X) = P(X|Y) * P(Y) / P(X)
当X条件独立时, P(X|Y) = P(X1|Y) * P(X2|Y) * ...
应用到猜名字上
P(gender=男|name=本山)
= P(name=本山|gender=男) * P(gender=男) / P(name=本山)
= P(name has 本|gender=男) * P(name has 山|gender=男) * P(gender=男) / P(name=本山)
计算
-
文件
charfreq.csv是怎么来的?曾经有个东西叫开房记录.avi(雾),里面有名字和性别, 2000w条, 统计一下得出
-
怎么算
P(name has 本|gender=男)?“本”在男性名字中出现的次数 / 男性字出现的总次数
-
怎么算
P(gender=男)?男性名出现的次数 / 总次数
-
怎么算
P(name=本山)?不用算(
不用算的根本原因是:女性和男性都要计算,但是只是分母,对于比较没有影响,所以即使去掉也没事,并不影响概率的比较)
项目解析:
在源码结构如下
- charfreq.csv
-
cli.py
- 可以作为运行的客户端
import argparse from .ngender import guess def main(): # 参数解读 parser = argparse.ArgumentParser( description='Guess gender for Chinese names') parser.add_argument('names', nargs='+', help='chinese names to guess') args = parser.parse_args() # 多个名字循环猜性别,然后打印猜测信息 for name in args.names: gender, prob = guess(name) print('name: {} => gender: {}, probability: {}' ''.format(name, gender, prob)) if __name__ == '__main__': main() -
ngender.py
- 核心代码,给出名字预测性别
#!/usr/bin/env python # -*- coding: utf-8 -*- import os __all__ = ['guess'] # 对于python2的unicode的兼容 def py2compat(name): try: name = name.decode('utf-8') except: pass return name class Guesser(object): def __init__(self): # 加载模型,获取字频数据 self._load_model() def _load_model(self): self.male_total = 0 self.female_total = 0 # 在这统计 self.freq = {} with open(os.path.join(os.path.dirname(__file__), 'charfreq.csv'), 'rb') as f: # skip first line,第一行是title next(f) for line in f: line = line.decode('utf-8') char, male, female = line.split(',') char = py2compat(char) self.male_total += int(male) self.female_total += int(female) self.freq[char] = (int(female), int(male)) # 计算总数 self.total = self.male_total + self.female_total # 计算每个字符出现在男中的概率和出现在女中的概率 for char in self.freq: female, male = self.freq[char] self.freq[char] = (1. * female / self.female_total, 1. * male / self.male_total) def guess(self, name): name = py2compat(name) # 去除姓 firstname = name[1:] # 在这个地方可以学习到检测中文的方法 for char in firstname: assert u'\u4e00' <= char <= u'\u9fa0', u'姓名必须为中文' # 分别计算是男概率和是女的概率 pf = self.prob_for_gender(firstname, 0) pm = self.prob_for_gender(firstname, 1) # 男概率大,就预测是男;女概率大,就预测是女 if pm > pf: return ('male', 1. * pm / (pm + pf)) elif pm < pf: return ('female', 1. * pf / (pm + pf)) else: return ('unknown', 0) def prob_for_gender(self, firstname, gender=0): # 三目运算 计算是男性占比和女性占比 p = 1. * self.female_total / self.total \ if gender == 0 \ else 1. * self.male_total / self.total # 朴素贝叶斯的核心:概率条件无关,直接相乘,得出名字是男性的概率或是女性的概率 for char in firstname: p *= self.freq.get(char, (0, 0))[gender] return p guesser = Guesser() def guess(name): return guesser.guess(name)特别收获(合法中文的检测)
# 在这个地方可以学习到检测中文的方法 for char in firstname: assert u'\u4e00' <= char <= u'\u9fa0', u'姓名必须为中文'