題目貌似取得有些大,準(zhǔn)確的說(shuō)應(yīng)該叫基于大數(shù)據(jù)的機(jī)器學(xué)習(xí),不過(guò)就這樣吧>_<
每當(dāng)說(shuō)到大數(shù)據(jù)分析,有些人就很諱莫如深,故意講得很復(fù)雜,生怕別人聽(tīng)懂就low了。然而其實(shí)可能沒(méi)那么復(fù)雜,就像陳皓說(shuō)的——所謂大數(shù)據(jù)不就是因?yàn)橛脖P(pán)便宜了,所以不用刪日志了嗎? <_<
好了,廢話不多說(shuō),這次實(shí)現(xiàn)的目標(biāo)很簡(jiǎn)單,就是通過(guò)已知數(shù)據(jù),通過(guò)某人的姓名分析出他的性別。如你所知,中國(guó)的姓名那是千千萬(wàn)萬(wàn),不像老外來(lái)來(lái)回回就是那幾個(gè)名字,拿個(gè)數(shù)據(jù)表對(duì)照一下就可以了——毛子更懶,男的叫朱可夫,女的就叫朱可娃,自帶性別標(biāo)識(shí),生怕別人以為自己是偽娘。。。
也許此刻你或許會(huì)問(wèn)這有什么用啊,簡(jiǎn)單來(lái)說(shuō)你想給自己的網(wǎng)站做一個(gè)性別統(tǒng)計(jì),并分析出各自的愛(ài)好,購(gòu)買(mǎi)習(xí)慣等,類(lèi)似淘寶每年的公眾那個(gè)統(tǒng)計(jì)報(bào)表,此時(shí)就可以用到——也許你會(huì)說(shuō)可以直接統(tǒng)計(jì)用戶(hù)信息里面啊,然而你確定有多少用戶(hù)會(huì)愿意填?又有多少是認(rèn)真的呢?不過(guò)有一個(gè)數(shù)據(jù)一般是真實(shí)的,那就是快遞單及收貨人姓名!
以下內(nèi)容均在win10+python2.7+MySQL5.5上驗(yàn)證通過(guò)。
數(shù)據(jù)分析,數(shù)據(jù)分析,沒(méi)數(shù)據(jù)當(dāng)然不行,所以程序未動(dòng),數(shù)據(jù)先行。本次我選擇的是某2000w的那個(gè)數(shù)據(jù)——不要問(wèn)我為什么會(huì)有,這不是本次的重點(diǎn)——之所以選擇這個(gè)是有一定理由的,這個(gè)我下面會(huì)說(shuō)到。
首先這次我們要做的是基于中文名的數(shù)據(jù),所以老外很多開(kāi)源的訓(xùn)練數(shù)據(jù)庫(kù)直接over了。。。然后要有足夠的基礎(chǔ)量,也就是說(shuō)作為訓(xùn)練數(shù)據(jù)庫(kù)應(yīng)該有200w左右的數(shù)據(jù),這樣可以保證數(shù)據(jù)的覆蓋面足夠廣。其次需要有自檢功能,也就是說(shuō)我能通過(guò)已知的數(shù)據(jù)去修復(fù)某些受損數(shù)據(jù),且不帶來(lái)其他任何影響,這點(diǎn)尤其重要!因此你可以選擇這個(gè)2000w或者某數(shù)字訂票的。。。
不過(guò)這個(gè)數(shù)據(jù)也并不是完美的,最明顯的一點(diǎn)就是本身其性別差就很大,男的多,女的少,而標(biāo)準(zhǔn)的訓(xùn)練數(shù)據(jù)應(yīng)該保證所有姓名的單字值總和為0,即未計(jì)算前這個(gè)名字是男是女各是0.5,不過(guò)影響不算特別大,可以通過(guò)程序修正部分。
導(dǎo)數(shù)據(jù)這部分我就不說(shuō)了,導(dǎo)完之后就是苦逼的洗數(shù)據(jù)。這是大頭,也是占這次示例里面時(shí)間最多的部分。
1、篩選出是中文名的數(shù)據(jù),并在info表添加一個(gè)is_China。
select * from info where not name regexp '^[1-9A-Za-z]';
或者直接刪除這部分不用的數(shù)據(jù)
delete from info where name regexp '^[1-9A-Za-z]';
2、進(jìn)行數(shù)據(jù)自檢
之前說(shuō)過(guò)之所以選擇這個(gè)數(shù)據(jù),一個(gè)很重要的原因就是可以自檢。因?yàn)槟憧梢酝ㄟ^(guò)身份證號(hào)來(lái)重新計(jì)算性別。
如果你之前已經(jīng)看過(guò)這個(gè)數(shù)據(jù)庫(kù),應(yīng)該會(huì)發(fā)現(xiàn)其中有很多的數(shù)據(jù)實(shí)際上是不對(duì)的,比如,你查看“雯”這個(gè)字,會(huì)發(fā)現(xiàn)幾乎男女的性別是各一半,這顯然是不對(duì)的——當(dāng)然也有一種可能,就是現(xiàn)在已經(jīng)基佬遍地了<_<
為此,首先第一步是將正確的身份證號(hào)篩選出來(lái),雖然你可以通過(guò)相互對(duì)照來(lái)修復(fù)部分身份證,但還是不推薦這么做,所以只需將不符合規(guī)則的身份證給剔除掉。
delete FROM info WHERE is_China = 1 AND CtfTp = 'ID' AND (CHAR_LENGTH(CtfId) <>18 and CHAR_LENGTH(CtfId) <>15)
當(dāng)有了正確的身份證之后,就可以進(jìn)行自檢修復(fù)了。
update info a inner join ( SELECT id, NAME, Gender, CASE (CASE CHAR_LENGTH(CtfId) WHEN 18 THEN substring(CtfId, 17, 1) ELSE substring(CtfId, 15, 1) END)%2 WHEN 0 THEN 'F' WHEN 1 THEN 'M' END sex, CtfId FROM `info` WHERE is_China = 1 AND CtfTp = 'ID') b set a.Gender = b.sex where a.id=b.id
以上SQL基本上沒(méi)什么難度,就是截取和更新。執(zhí)行的時(shí)候,你可以出去喝杯咖啡,或者運(yùn)動(dòng)一番,又或是去趟衛(wèi)生間。。。反正什么打發(fā)時(shí)間做什么。
基本上上述工作做完,你的可用數(shù)據(jù)在180w左右。
3、建立字符表,將文本轉(zhuǎn)化為數(shù)值
首先先建表
CREATE TABLE `name` ( `id` int(11) NOT NULL AUTO_INCREMENT, `chars` varchar(2) COLLATE utf8_unicode_ci DEFAULT NULL, `val` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `IDX_NAME` (`chars`,`val`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
這里我偷懶選擇了將值直接插入數(shù)據(jù)庫(kù),即每個(gè)文字都是有多行的,而不是分類(lèi)統(tǒng)計(jì)的方法,如果你有興趣可以試著改寫(xiě)一下表結(jié)構(gòu),val變成三列——男、女、總和,使得讀取更加快速方便。
接著我們就要往里面灌數(shù)據(jù)了。雖然用復(fù)雜的SQL應(yīng)該也能實(shí)現(xiàn),不過(guò)這里我們使用python來(lái)完成這項(xiàng)工作。如果你對(duì)python還不是很熟悉,可以去這里學(xué)習(xí)一下
#encoding=utf-8import sysimport MySQLdbimport randomimport timeimport tracebackimport mathreload(sys)sys.setdefaultencoding('utf-8') #設(shè)置Python的默認(rèn)編碼為 utf-8########################################################################db=MySQLdb.connect(host="localhost",user="root",passwd="",charset='utf8')cur=db.cursor(cursorclass = MySQLdb . cursors . DictCursor ) #讓返回的是字典類(lèi)型cur.execute('use 2000w')cur.execute('SELECT id,Name,Gender FROM `info` WHERE is_China=1;')rets=cur.fetchall()chars=[]chars_gender=[]for ret in rets: length=len(ret['Name'])/2 char=ret['Name'][length:] for x in xrange(length,len(char)+1): c=char[x-1:x] chars.append(c); sex=0 if(ret['Gender']=='M'): sex=1 else: sex=-1 chars_gender.append(sex)for k in xrange(0,len(chars)): sql="INSERT INTO name (chars, val) VALUES ('"+chars[k]+"',"+(str)(chars_gender[k])+");" # print sql try: cur.execute(sql) db.commit() #加上這句之后才會(huì)提交執(zhí)行,否則不執(zhí)行 except: # 發(fā)生錯(cuò)誤時(shí)回滾 db.rollback()
現(xiàn)在知道我為什么偷懶了吧,因?yàn)檫@樣可以少些很多行,順帶可以不用動(dòng)腦筋——哈哈哈哈哈哈哈哈——pia飛。。。
接著你又可以去喝杯咖啡,上趟廁所了。。。
至此,我們的數(shù)據(jù)處理部分就全部結(jié)束了。是不是很簡(jiǎn)單呢。理論上應(yīng)該沒(méi)什么不容易理解的,那么,OK,下面就是使用這些數(shù)據(jù)了。
模型設(shè)計(jì)。老實(shí)說(shuō),這部分一般都是由Scientist來(lái)設(shè)計(jì)完成的,不過(guò)這次我們不需要這么復(fù)雜,就用最簡(jiǎn)單的條件概率就OK了。
條件概率,如果你還記的高中或大學(xué)的知識(shí)的話應(yīng)該知道,他是計(jì)算在某條件下某事發(fā)生的概率。因此我們將這次的命題轉(zhuǎn)化一下就可以知道,其實(shí)質(zhì)是已知我們選了某字則該字是屬于女生/男生的概率。至于公式的話,自己翻書(shū)吧。
好,接下來(lái)就到了coding時(shí)間了。廢話不多說(shuō),直接上代碼
#encoding=utf-8import sysimport MySQLdbimport randomimport timeimport tracebackimport mathimport decimalreload(sys)sys.setdefaultencoding('utf-8') #設(shè)置Python的默認(rèn)編碼為 utf-8########################################################################name=raw_input('please input name:');name=name.decode("gbk")chars=[]length=len(name)/2char=name[length:]for x in xrange(length,len(char)+1): c=char[x-1:x] chars.append(c);# 簡(jiǎn)單的疊字處理修正chars_unique=set(chars)is_overlapping=Falseif len(chars_unique)!=len(chars): is_overlapping=Truedb=MySQLdb.connect(host="localhost",user="root",passwd="",charset='utf8')cur=db.cursor(cursorclass = MySQLdb . cursors . DictCursor ) #讓返回的是字典類(lèi)型cur.execute('use 2000w')# sql="SELECT Gender,COUNT(Name) total_num FROM `info` WHERE is_China=1 GROUP BY Gender"# cur.execute(sql)# rets=cur.fetchall()# F_total_num=rets[0]['total_num']# M_total_num=rets[1]['total_num']# total_num=M_total_num+F_total_num# 加速運(yùn)行 每次都去統(tǒng)計(jì)其實(shí)意義不大,因?yàn)楫?dāng)數(shù)量足夠大時(shí),微小的變化并不會(huì)影響統(tǒng)計(jì)結(jié)果,因?yàn)樵谀侵熬纫呀?jīng)不足了total_num=1870562F_total_num=562496M_total_num=1308066pro1_arr=[]pro2_arr=[]overlapping_Word=''for x in xrange(0,len(chars)): sql="SELECT SUM(val) v FROM `name` WHERE chars='" +chars[x]+ "' GROUP BY val" cur.execute(sql) rets=cur.fetchall() if(len(rets)==1): if(rets[0]['v']>=0): m_nums=rets[0]['v'] f_nums=0; else: f_nums=rets[0]['v'] m_nums=0; else: try: f_nums=rets[0]['v'] except: f_nums=0 try: m_nums=rets[1]['v'] except: m_nums=0 if f_nums==0 and m_nums==0: # 此處為不存在的字符做異常處理,可以調(diào)用類(lèi)似createSex.py的方法將不存在的數(shù)據(jù)加入那么表 print "Sorry,I don't know the name sex,maybe you can help me" sys.exit(0) char_total_num=abs(m_nums)+abs(f_nums) pro1_val=(abs(f_nums)/total_num)/(char_total_num/total_num) pro2_val=(abs(m_nums)/total_num)/(char_total_num/total_num) pro1_arr.append(pro1_val) pro2_arr.append(pro2_val) if overlapping_word==chars[x]: if pro1_val > 0.8 or pro2_val > 0.8: is_overlapping=False else: overlapping_word=chars[x]pro1=sum(pro1_arr)/len(chars);pro2=sum(pro2_arr)/len(chars);if is_overlapping: pro1+=decimal.Decimal(0.3) pro2-=decimal.Decimal(0.3)
if pro1>decimal.Decimal(1): pro1=decimal.Decimal(1); pro2=decimal.Decimal(0);
if pro1>pro2: print "The Name have "+(str)(pro1*100)+" is a woman name";else: print "The Name have "+(str)(pro2*100)+" is a man name";
看到這里,也許有人會(huì)說(shuō),臥槽,這不是坑爹嗎?就這100來(lái)行的代碼就完成了?這他喵的也算大數(shù)據(jù)分析?但是,why not ?
好了,收起無(wú)謂的口水仗吧,來(lái)看看我們的程序做了什么。第一將新的名字處理成單字,然后分別再去計(jì)算每個(gè)單字的條件概率,最后將所有的概率相加然后平均。然后還為疊字做了一些小小的處理。別看雖然代碼不多,但如果只看結(jié)果的話,還是可以接受的,基本上可以達(dá)到80%以上的識(shí)別率(日文名字不行,哪怕是漢字,不要問(wèn)我為什么——自己往前看,當(dāng)然有例外,比如西木野真姬<_<)。
是不是感覺(jué)很簡(jiǎn)單!蹭蹭幾下,你就會(huì)大數(shù)據(jù)分析了!(偽

如你所見(jiàn),除了陸無(wú)雙判斷有些失誤之外其他均可以,當(dāng)然這里還有一部分原因是由于之前提到過(guò)的本身數(shù)據(jù)問(wèn)題造成的,還有部分是程序的原因,比如這邊計(jì)算總的概率時(shí)只是簡(jiǎn)單的相加平均,這會(huì)導(dǎo)致某些字由于兩極分化嚴(yán)重而導(dǎo)致總結(jié)果的不準(zhǔn)確,因此應(yīng)該才用加權(quán)計(jì)算來(lái)消磨掉這部分的錯(cuò)誤。當(dāng)然其他方法也是有的,我在這里只是做一個(gè)拋磚引玉的作用,如果各位有興趣或者有更好的思路歡迎來(lái)這里,互相探♂討♂交♂流
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注