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
Přehled komentářů