Odhad pohlaví z celého jména

Při práci s daty reálných lidí můžeme narazit na problém určení jejich pohlaví. Pokud chceme použít například personalizované oslovení „vážená paní / vážený pane“, musíme mít informace o pohlaví uložené v záznamech, nebo se je můžeme pokusit určit. V článku si ukážeme takový algoritmus na určování pohlaví.
Nálepky:
Problém
Problému odhadu pohlaví na základě jména se věnuje mnoho webových stránek a několik specializovaných nástrojů, ale většinou vrací jenom výsledky bez popisu algoritmu, výsledků testů na reálných datech a dalších podrobností. Tento článek popisuje algoritmus, vysvětluje ho, uvádí výsledky i porovnání se specializovaným nástrojem na velkém objemu reálných dat.
Uvažujme situaci, že na základě textového řetězce potřebujeme odhadnout hodnotu pro daný parametr ze 2 možností. Bez újmy na obecnosti odhadujme pohlaví na základě celého jména, přičemž pod celým jménem budeme chápat zejména křestní jméno a příjmení, ale celé jméno může obsahovat i další atributy jako například titul nebo občanství.
Algoritmus
Základní princip je zřejmý a jednoduchý. Zjistíme, zda se dané jméno nachází v seznamu typicky mužských nebo typicky ženských jmen, a pokud je výsledek jednoznačný, uděláme odhad. V češtině, ale i v jiných slovanských jazycích, je možné odhadnout pohlaví i na základě přechýleného příjmení, naproti tomu v některých jazycích je struktura celého jména odlišná a nemůže se na něj dívat jako na jednoduchou kombinaci jednoslovného křestního jména a jednoslovného příjmení. Strukturu jmen a přechylování v algoritmu ale nevyužívám.
Dále předpokládejme, že celé jméno se skládá ze slov a současně platí:
- Je silná závislost mezi slovy celého jména a pohlavím
- Máme k dispozici velké množství dat, kde je vyplněné celé jméno a pohlaví – z této množiny vytvoříme slovník
- Množina dat, pro kterou chceme odhadovat pohlaví na základě celého jména má stejné rozdělení slov a pohlaví jako množina, z které jsme vytvořili slovník (není dobré odhadovat pro česká jména pohlaví na základě vietnamského slovníku)
K takové situaci běžně dochází v prostředí, kde je manuálně pořizováno velké množství dat (například v bankách, pojišťovnách) a do některých aplikací není možné údaj o pohlaví zadat, případně někteří pracovníci údaj o pohlaví zadávají a jiní nikoliv. Tahle situace je známá mnohým uživatelům datových skladů, proto jsem v článku použil jako jazyk PL/SQL.
Za uvedených předpokladů je možné použít níže popsaný algoritmus.
Nejprve vytvoříme slovník. Všechna celá jména standardizujeme použitím Oracle funkcí upper (velká písmena), trim (ořezání mezer), translate (nahrazení tečky a čárky mezerou), convert (zrušení diakritiky) a regexp_replace (nahrazení násobných mezer 1 mezerou)[1]. Mezery chápeme jako oddělovače jednotlivých slov standardizovaného celého jména.
Ze standardizovaných slov z celých ženských jmen vytvoříme slovník pro ženy a jednotlivým standardizovaným slovům přiřadíme skóre pomocí Oracle funkce percent_rank [3]. Analogickým způsobem vytvoříme slovník pro muže.
Pro lepší pochopení se podívejte do přílohy chicago_marathon.xls.
Slovník pro jména a příjmení na základě pohlaví včetně četnosti pro ČR je volně dostupný na stránkách MV ČR (v algoritmu jsem tento slovník nevyužil) [2].
Pro odhadnutí pohlaví na základě celého jména propojíme toto jméno s ženským a mužským slovníkem a pro oba slovníky zjistíme celkové souhrnné skóre. Pokud je absolutní rozdíl mezi souhrnným skóre pro mužský a ženský slovník větší jako epsilon (kde epsilon je 0 nebo malé kladné číslo), tak uděláme odhad na základě většího skóre.
Tento algoritmus nebere do úvahy význam dat, pořadí slov v celém jméně, kombinace s jinými entitami, kvalitu zdroje, znalosti z jiných oblastí a podobně. Proto výsledky nebudou tak dobré jako při správné analýze na základě významu dat.
Na druhé straně jsme často v situaci, že nemáme dostatečné informace o významu dat, jsme v prostředí, které neznáme a nemáme pro něj vhodnou customizaci běžných nástrojů, nemáme kvalitní slovníky a podobně. V tomto případě nám algoritmus vrátí relativně dobré výsledky bez velké námahy a hlavně respektuje rozdělení dat na vašem zdroji.
Výsledky a porovnání se specializovaným nástrojem
Algoritmus jsem otestoval na reálných datech. Pro první test jsem vybral výsledky z maratonu v Chicagu v roce 2011 (jsou dostupné na oficiálních stránkách maratonu), kde jsem vzal do úvahy celé jméno, které obsahovalo i občanství [5]. 37 547 jmen jsem rozdělil na 2 disjunktní skupiny, přičemž první skupina obsahovala 80 % běžců a sloužila k vytvoření slovníku, zbylých 20 % tvořilo druhou skupinu, kterou jsem použil ke kontrole výsledků algoritmů.
Algoritmus správně určil pohlaví 6 543 ze 7 157 běžců (pro epsilon rovné nule).
Za správný odhad považuji případ, že správně odhadnu pohlaví. Za špatný odhad považuji případ, že odhadnu opačné pohlaví. Pro zbytek do 100% algoritmus neudělal odhad.
Chicago marathon |
Algoritmus |
|||
epsilon = 0 |
epsilon = 0.2 |
|||
Správný odhad |
Špatný odhad |
Správný odhad |
Špatný odhad |
|
Pohlaví |
91,421% |
6,371% |
72,684% |
2,124% |
Pro tento test je v příloze k dispozici úplný zdrojový kód a přehledné výsledky.
Druhý test probíhal na reálných českých datech, kde jsem na základě celého jména odhadoval pohlaví (pouze pro fyzické osoby) a pak i typ osoby (fyzická nebo právnická osoba). Celkový počet záznamů byl výrazně vyšší než u maratonu a mohutnost každé skupiny byla minimálně v desítkách tisíc. Výsledky algoritmu jsem porovnal s výsledky, které vrátil SAS Dataflux customizovaný pro Českou republiku. Algoritmus i SAS Dataflux běžely nad stejnou množinou dat. Pro detailnější přehled jsem změřil výsledky jak pro české občany (firmy), tak i pro cizince.
Reálná data |
Algoritmus |
Dataflux |
||||
epsilon = 0 |
epsilon = 0.2 |
|||||
Správný odhad |
Špatný odhad |
Správný odhad |
Špatný odhad |
Správný odhad |
Špatný odhad |
|
Pohlaví – Celkově |
98,278% |
0,236% |
95,055% |
0,117% |
94,050% |
0,210% |
Pohlaví – Češi |
99,597% |
0,052% |
97,095% |
0,026% |
99,007% |
0,057% |
Pohlaví – Cizinci |
88,993% |
1,531% |
80,695% |
0,754% |
59,156% |
1,292% |
Typ osoby – Celkově |
98,676% |
0,400% |
88,232% |
0,211% |
98,749% |
0,890% |
Typ osoby – Češi |
99,397% |
0,233% |
89,392% |
0,121% |
98,881% |
0,842% |
Typ osoby – Cizinci |
91,611% |
2,037% |
76,869% |
1,091% |
97,450% |
1,363% |
Vidíme, že algoritmus i Dataflux fungují výborně pro odhad pohlaví českých občanů, u cizinců je algoritmus výrazně lepší než česká verze Datafluxu.
Pokud se podíváme na odhad typu osoby, tady vidíme, že pro české občany/firmy funguje Dataflux i algoritmus s nulovým epsilon výborně. SAS Dataflux je lepší v odhadech cizích občanů/firem. Lepší výsledky algoritmu pro česká data se dá vysvětlit výrazně vyšším počtem záznamů než u cizinců, co se projevilo i v kvalitě slovníku a lepším skórováním jednotlivých standardizovaných slov.
Taky vidíme (jak jsme mohli předpokládat), že pro větší epsilon je algoritmus konzervativnější. Tj. vrací menší počet špatných, ale i správných odhadů.
Existuje více metod, jak algoritmus vylepšit. Například ignorovat ve slovníku slova s malým skóre, určit vhodné epsilon na základě počtu záznamů ve slovníku a počtu slov v celém jméně, uvažovat bayesovský pohled pro případ s velmi výrazným rozdílem mezi počtem mužů a žen v testované skupině, uvažovat více než 2 možné výsledky a podobně. Nicméně všechny tyto kroky výrazně snižují největší výhodu algoritmu – jeho jednoduchost.
Implementace v PL/SQL
Pro jednoduchost a přehlednost ukážu jenom základní principy, neuvažuji výjimky a ani speciální případy. Úplný zdrojový kód pro maraton je v příloze.
Mějme tabulky TABLE_OF_WOMEN_FULL_NAMES a TABLE_OF_MEN_FULL_NAMES s jedním varchar2 sloupcem.
Spustíme create_dictionary(‚TABLE_OF_WOMEN_FULL_NAMES‘, ‘DICTIONARY_WOMEN’) a create_dictionary(‚TABLE_OF_MEN_FULL_NAMES‘,‘DICTIONARY_MEN’), kde
CREATE OR REPLACE Procedure create_dictionary(table_name_in In user_tables.table_name%Type, table_name_out In varchar2) Is Type tabtype Is Table Of Varchar(2000); input_table tabtype; words Varchar(2000); count_of_words Number; Begin --create table for dictionary Execute Immediate 'create table '|| table_name_out ||' (word VARCHAR2(200))'; Execute Immediate 'select * from ' || table_name_in Bulk Collect Into input_table; For i In input_table.first .. input_table.last Loop --(1) --space, comma and dot are separated symbols for --words we replace them with space by using translate --multiple space we replace with single space by --using regexp_replace --standartization of word by using upper and --convert oracle functions words := upper(convert(Trim(regexp_replace(translate(input_table(i),',.',' '),'( ){2,}',' ')),'US7ASCII')) || ' '; If words = ' ' Then count_of_words := 0; Else count_of_words := length(words) - length(Replace(words, ' ')); End If; For j In 1 .. count_of_words Loop --(2) --inserted j-th standarted word from full name to dictionary If j = 1 Then Execute Immediate 'insert into ' || table_name_out || ' values ('''||substr(words,1,instr(words, ' ', 1, 1) - 1) ||''')'; Else Execute Immediate 'insert into ' || table_name_out || ' values (''' || substr(words,instr(words, ' ', 1, j - 1) + 1, instr(words, ' ', 1, j) - instr(words, ' ', 1, j - 1) - 1) || ''')'; End If; End Loop; --(2) Commit; End Loop; --(1) End;
Pro hodnoty z dictionary_men přiřadíme skóre pomocí percent_rank. Používám dvakrát union all, abych dal všem slovům pozitivní skóre.
select word, PERCENT_RANK () OVER (order by count(*)) score from (select word from DICTIONARY_MEN union all select word from DICTIONARY_MEN union all select null from dual) group by word
Pro odhad pohlaví použijeme následující select:
select t1.full_name, case when t1.men_score-t2.women_score>&epsilon then 'male' when t2.women_score-t1.men_score>&epsilon then 'female' else 'no result' end result from --full_name_without_gender --is table with full names we need to estimate gender --men and --woman are tables with words from dictionaries and score (select t.full_name ,sum(nvl(men.score,0)) men_score from full_name_without_gender t left join (select word, PERCENT_RANK () OVER (order by count(*)) score from (select word from DICTIONARY_MEN union all select word from DICTIONARY_MEN union all select null from dual) group by word) men on ' '||upper(convert(translate(full_name,',.-',' '),'US7ASCII'))||' ' like '% '||men.word||' %' group by t.full_name) t1 join (select t.full_name ,sum(nvl(women.score,0)) women_score from full_name_without_gender t left join (select word, PERCENT_RANK () OVER (order by count(*)) score from (select word from DICTIONARY_WOMEN union all select word from DICTIONARY_WOMEN union all select null from dual) group by word) women on ' '||upper(convert(translate(full_name,',.-',' '),'US7ASCII'))||' ' like '% '||women.word||' %' group by t.full_name) t2 on t1.full_name=t2.full_name
Tento select s joinem přes like je velmi pomalý, zvlášť pro velký objem dat. Na datech z chicagského maratonu běží několik minut. Pro zrychlení je možné jednotlivá jména nejprve rozdělit a pak spustit upravený select nebo použít jiný způsob na optimalizaci selectu.
Přílohy:
chicago_marathon.xls
inserting script.sql
chicago.sql
Zdroje:
[1] Oracle Database SQL Reference 10G Release 1 (10.1), Chapter SQL Functions
http://docs.oracle.com/cd/B14117_01/server.101/b10759/functions001.htm#i88893
[2] Četnost jmena a příjmení
http://www.mvcr.cz/clanek/cetnost-jmen-a-prijmeni-722752.aspx?q=Y2hudW09MQ%3d%3d
[3] Oracle Database Data Warehousing Guide 10g Release 2 (10.2), Chapter 21 – SQL for Analysis and Reporting
http://docs.oracle.com/cd/B19306_01/server.102/b14223/analysis.htm
[4] Dataflux Technology Overview Course Notes
[5] Bank of America Chicago marathon 2011 results
http://results.public.chicagomarathon.com/2011/
[6] Estimate gender from full name with Oracle – anglická verze
http://brejcak.blogspot.com/2011/10/estimate-gender-from-full-name-with.html
A ne jen kýč pro rychlokvašky a také programátory. Aneb, vzpomínáte na brilantní článek o pojídačích sušenek?
Díky :-)
Jo, ale ten je nejmíň 40 let starý :-)))
Což neubírá jeho nadčasovosti.
Dodnes se vždy ptám sám sebe, jestli jsem někde neudělal chybu a jestli ten co to jednou po mně bude louskat (bude-li to potřeba) tomu bude rozumět.
Někdy je lepší vyžadovat více od sebe samého než-li od ostatních. Inu, sušenky se samy neupečou. Ačkoliv to mnohým dnes připadá jako samozřejmost.
Nebo snad ne?
Houby, tu je dostupny aj zdrojak. Toto zvladne kazda lama.
Naviac, PL/SQL je ako ten hnusny Pascal.
??? k čemu toto ???, není lepší a přesnější (100%) male/female ~ boolean v tabulce se jménem ?
Já jsem se napřed naučil číst a teprve potom psát. Ty jsi na to evidentně šel obráceně :)
Ano je jednodušší mít male/female v tabulce. Ale tohle je ukázka jak zjistit pohlaví osoby dle jména když pohlaví není uvedeno. A když se to nezdaří, objeví se „no results“. Otázka jak často se tohle objeví (lepší varianta) nebo jak často bude odpověd nesprávná (mnohem horší varianta).
Tracy Pearson… M nebo F?
Saša Jirků… M nebo F?
Marti Pavlů… M nebo F?
Ty nedostaneš právě výsledek u 27 procent položek noresult. Ty podle článku dostaneš 27 procent položek neurčil jsem, nu ale nemáš zaručeno v tom zbytku 73 procent jistotu určení Male/Female. Ty dostaneš pouze jistotu, že by jsi měl statisticky dostat správnou odpověď na 73 procent. A tímto projitím jsi tuto nejistotu snižil, ale nevyloučil chybu. Což podle mě program musí pokud se tváří, že to dělá.
Takže pokud tam nedáš položku male/female, je toto vhodné jenom např. policejní složky, spamery nebo obchodní přehledku. Jinak je toto pro praktické využití zbytečnost.
Prakticky se to vyuziva pri rozesilani spamu ;D, jednoduse mate desitky tisic adres (at uz realnych nebo mailovych) a protoze si marketaci vymejslej, ze kazdyho treba oslovit, tak se resi, kdo je chlap/zenska, paac tyhle informace se pri ziskavani tech dat vetsinou nikam nezapisujou ….
Přesně jak píšete, u slovanských jmen to bylo v příjmení, což některé „moderní“ ženy nechtějí, i na západě se začala dávat jména mužská ženám a naopak.
Takže když už to někdo dá do databáze ví jestli je to muž žena a prostě dá o „byte“ více do DBF a nemusí se vymýšlet takovéto šílenosti co žerou výkon a stojí čas programátora co se musí zaplatit.
Tedy pokud toto neřešíte v Rigest Digest nebo jak se jmenuje ta firma či podobných firmách nebo spam firma…
Jak jsem uvedl, algoritmus je obecný pro odhad parametru na základě textového řetězce (pokud tento parametr neznáme nebo má špatnou kvalitu) takže může pomoci například:
• klientovi nabídnout časopis pro muže nebo pro ženy
• pokud potřebujeme zjistit, zda je klientem firma (s.r.o.) nebo fyzická osoba – například z důvodu zamezení podvodům, nabízení špatného produktu, správného ratingu pro půjčku
• pokud chceme klientovi nabídnout knihu, které ho bude zaujímat a rozhodujeme se mezi několika možnostmi (na základě názvu knih, které si od nás koupil, nebo na které se díval)
• abychom zjistili, v jakém jazyce je napsána webová stránka a na základě toho upravili výsledky vyhledávání (na základě prvních 2 vět textu)
• pro kontrolu, že jsou data uvedeny správně
• abychom odhadli, co našeho klienta zajímá, a zobrazili reklamu, která bude mít lepší click-rate
• abychom vylepšili kvalitu dat
Samozřejmě tyto problémy se dají řešit elegantněji, jiným typem odhadu a žádný nebude 100%.
Ale odhady jsou všude kolem nás, například když googlujete, tak google odhaduje, co je pro vás nejdůležitější, taky odhaduje, zda jste neudělali překlep a nabídne vám jiný výsledek (funkce did you mean?)
Díky za něj. Přidám ještě odkazy na:
https://metacpan.org/module/Text::GenderFromName
https://github.com/petewarden/genderfromname
Je to hezké, ale myslím, že vyšší přesnosti by se dosáhlo, kdyby se ze slovníku neořezávala diakritika + vyřešil by se jako speciální případ, když je jméno zadané bez diakritiky.
Ale asi se někdo chce bavit – OK, nic proti tomu nemám.
Jen dva příklady. Třeba v USA je Dana mužské i ženské jméno, zatímco např. v Portugalsku je Maria taktéž mužské a i ženské jméno (asi i ve španělsky mluvících zemích.
http://en.wikipedia.org/wiki/Unisex_name
1) Nebylo by lepší pro výpočet skóre místo percent_rank použít relativní četnosti? Tedy nahradit:
PERCENT_RANK () OVER (order by count(*))
za:
count(*) / sum(count(*)) over ()
.Navíc by potom odpadla potřeba kejklí s UNION ALL.
2) Vzhledem k tomu, že se skóre porovnává s epsilon, nebylo by vhodnější AVG() místo SUM()? Takto jsou zvýhodňována delší jména.
Ahoj Kamil,
s AVG() miesto SUM() súhlasím.
count(*) / sum(count(*)) over () by nefugoval pri výraznom rozdiele počtu rôznych ženských a mužských mien.
Miesto percent_rank by šlo použiť rank/(select count(distinct WORD) from dictionary) over (), dala by to v podstate totožné výsledky.
Peter
Jelikoz autor o sobe pise, ze se zabyva datovou kvalitou, rekl bych, ze hlavni vyuziti je kontrola databazi a hledani podezrelych zaznamu. Ti co se tu ptaji, k cemu je to dobre zrejme predpokladaji, ze pocitace jsou neomylne a v databazich nejsou chyby.
Pocitace jsou dost neomylna zarizeni, horsi je to s kvalitou periferie mezi zidli a klavesnici :-).
Pro nějakou kontrolu kvality dat (tedy v tom smyslu, zda odpovídají realitě) je toto prakticky bezcenné, protože fakticky ani lidský operátor nebo administrátor nemůže nikdy s jistotou podle jména říct, zda pohlaví uvedené v databázi odpovídá realitě. V případě českých jmen a příjmení je samozřejmě výrazně jednodušší to odhadnout, ale je to stále jen odhad.
Nicméně v obecnějším smyslu jde skutečně o nástroj pro zvýšení kvality dat – jejich obohacení o informaci, která v nich dříve explicitně nebyla k dispozici. Tato informace samozřejmě není kvalitní ve smyslu „ověřená“, nicméně i tak může mít slušnou hodnotu, pokud se vhodně využije. Podle mé zkušenosti se údaj o pohlaví zjištěný takovým způsobem užívá velmi často jednak pro PR a marketing, jednak (už méně často) pro diskriminaci podle pohlaví. V těchto případech obvykle až tak moc nezáleží na tom, jestli se v nějakém tom procentu trefíme vedle.
Hmm, zajímavý problém. Jak by asi obstálo počítání četnosti trigramů? Asi si to zkusím večer…
Jsem napsal algoritmus ktery z data a pocasi odhadne s 50% uspesnosti zda v ten den budu mit rande nebo ne
Z rande ktere jsem mel bylo 50% predpovezeno
A 50% prorokovanych rande se skutecne vyplnilo
Pocasi je teplota, tlak, osvetleni a relativni vlhkost.
Jak velka byla testovaci mnozina?
Please try again later. A prispevek v prdeli.
Pokud by byl podobný, jako ten váš předchozí, tak možná pánbůh zaplať za to… Víc takových dobře načasovaných server errorů. A možná bych i odstranil tu výzvu try again latter.
Tak a ted mi reknete. Jak resite zmenu pohlavi? A neni to vubec specialni pripad jak by se mohlo zdat. Nicmene pro pany programatory to zatim v informacnich systemech byl vzdy orisek, protoze vetsinou apriori predpokladali ze ke zmene u dane polozky nedojde. Vetsinou to resil manualni UPDATE. Jak resite rozpoznani pohlavi kdyz si transexual zvoli pohlavne neutralni jmeno? To ze spousta IS vubec nepredoklada zmenu rodneho cisla ci primo identifikuje pohlavi podle nej je uz vec poddruzna.