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í.

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_marat­hon.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_FUL­L_NAMES a TABLE_OF_MEN_FUL­L_NAMES s jedním varchar2 sloupcem.

Spustíme create_dictio­nary(‚TABLE_OF_WO­MEN_FULL_NAMES‘, ‘DICTIONARY_WO­MEN’) a create_dictio­nary(‚TABLE_OF_MEN_FUL­L_NAMES‘,‘DIC­TIONARY_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_marat­hon.xls
inserting script.sql
chicago.sql

Zdroje: 

[1] Oracle Database SQL Reference 10G Release 1 (10.1), Chapter SQL Functions
http://docs.o­racle.com/cd/B141­17_01/server.101/b10759/­functions001.htm#i88893
[2] Četnost jmena a příjmení
http://www.mvcr­.cz/clanek/cet­nost-jmen-a-prijmeni-722752.aspx?q=Y2hud­W09MQ%3d%3d
[3] Oracle Database Data Warehousing Guide 10g Release 2 (10.2), Chapter 21 – SQL for Analysis and Reporting
http://docs.o­racle.com/cd/B193­06_01/server.102/b14223/­analysis.htm
[4] Dataflux Technology Overview Course Notes
[5] Bank of America Chicago marathon 2011 results
http://results­.public.chica­gomarathon.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

Vystudoval matematickou statistiku na MFF UK, Pracuje jako matematik pro vyhledávač hotelů trivago

Komentáře: 25

Přehled komentářů

100% Lenin Konečně něco pro skutečné programátory
Ivan Nový Re: Konečně něco pro skutečné programátory
100% Lenin Re: Konečně něco pro skutečné programátory
balki Re: Konečně něco pro skutečné programátory
nuny ??? k čemu toto ???
Petr Re: ??? k čemu toto ???
Gorila Re: ??? k čemu toto ???
Karol Re: ??? k čemu toto ???
j Re: ??? k čemu toto ???
Karel Re: ??? k čemu toto ???
Peter Brejčák Re: ??? k čemu toto ???
pek Pěkný článek
Michal Illich Neořezávat diakritiku
X125 Taky nevím, k čemu je to dobré
vembloud PERCENT_RANK, SUM()?
Peter Brejčák Re: PERCENT_RANK, SUM()?
JS Re: Odhad pohlaví z celého jména
Jerry12 Re: Odhad pohlaví z celého jména
Ondra Re: Odhad pohlaví z celého jména
Ladislav Thon Re: Odhad pohlaví z celého jména
Clock Odhad rande z data a pocasi
JS Re: Odhad rande z data a pocasi
Clock Internal server error has occured
Petr Re: Internal server error has occured
Trident Re: Odhad pohlaví z celého jména
Zdroj: https://www.zdrojak.cz/?p=3585