Skip to content

lixo, gerando abrev 3 letras

Peter edited this page Nov 18, 2018 · 8 revisions
CREATE SCHEMA IF NOT EXISTS abbrev;

CREATE FUNCTION abbrev.consoants(
	p_x text,  -- any input word
	p_preserveFirst boolean DEFAULT true  -- flag
) RETURNS text AS $f$
  SELECT string_agg(x,'') 
  FROM regexp_split_to_table($1,'') WITH ORDINALITY t (x,a) 
  WHERE (p_preserveFirst AND a=1) OR x not IN ('A','E','I','O','U') 
$f$ LANGUAGE SQL IMMUTABLE;

CREATE or replace FUNCTION abbrev.drop_prep_pt(
	p_words text[]  -- any input word
	-- ,p_lang text DEFAULT 'pt'  -- flag
) RETURNS text[] AS $f$
  SELECT array_agg(replace(x,E'D\'','')) 
  FROM unnest($1) WITH ORDINALITY t(x,a)
  WHERE a=1 OR x not IN ('E', 'DA', 'DE', 'DO', 'DAS', 'DOS')
$f$ LANGUAGE SQL IMMUTABLE;

----

CREATE or replace FUNCTION abbrev.letters3_by_rule_from_array(
	p_list text[], -- the name spplited into array
	p_rule text -- the rule to be used to obtain the 3-letter abbreviation 
) RETURNS text AS $f$
   SELECT substr(x,1,3)
   FROM ( SELECT CASE -- 1=first word, 9=last word
     WHEN $2='cut1' THEN  array_to_string($1,'')
     WHEN $2='init1_cutcons1' THEN  abbrev.consoants( array_to_string($1,''), true )
     WHEN $2='init1_cut2' and array_length($1,1)>1 
          THEN substr($1[1],1,1) || array_to_string($1[2:],'')
     WHEN $2='inits12_init9'  and array_length($1,1)>2  
          THEN substr($1[1],1,1) || substr($1[2],1,1) || substr($1[array_upper($1,1)],1,1)
     WHEN $2='cut1last1' AND length($1[1])>2 
          THEN substr($1[1],1,2) || substr($1[1], length($1[1]), 1)
     WHEN $2='cut9' AND length($1[1])>1 THEN  $1[array_upper($1,1)]
     WHEN $2='init1_cut9' AND length($1[1])>1 
          THEN substr($1[1],1,1) || $1[array_upper($1,1)]
     WHEN $2='init9_cutcons' AND length($1[1])>1 
          THEN abbrev.consoants( $1[array_upper($1,1)], true )
     WHEN $2='init1_cutcons9' AND length($1[1])>1 
          THEN substr($1[1],1,1) ||  abbrev.consoants( $1[array_upper($1,1)], true )
     ELSE NULL
     END
   ) t(x)
   WHERE length(x)>2
$f$ LANGUAGE SQL IMMUTABLE;

CREATE or replace FUNCTION abbrev.apply_rules(
	p_x text  -- any city name. E.g. 'São José dos Campos' or 'Manaus'
) RETURNS jsonb AS $f$
 SELECT jsonb_object_agg(val,rule)
 FROM (
  SELECT *, lag(val) OVER (order by val,rule) dupval
  FROM regexp_split_to_array( upper(unaccent(trim(p_x))) , '[\s\.,;\-]+') t(x),
  LATERAL ( VALUES 
    ('r01', abbrev.letters3_by_rule_from_array(x,'cut1') )
    ,('r02', abbrev.letters3_by_rule_from_array(abbrev.drop_prep_pt(x),'inits12_init9') )
    ,('r03', abbrev.letters3_by_rule_from_array(abbrev.drop_prep_pt(x),'init1_cut2') )
    ,('r04', abbrev.letters3_by_rule_from_array(x,'init1_cutcons1') )
    ,('r15', abbrev.letters3_by_rule_from_array(x,'cut9') )
    ,('r16', abbrev.letters3_by_rule_from_array(x,'init9_cutcons') )
    ,('r17', abbrev.letters3_by_rule_from_array(x,'cut1last1') )
  ) t2(rule,val) 
  WHERE x IS NOT NULL AND val IS NOT NULL
 ) t3 
 WHERE dupval is null OR dupval!=val
$f$ LANGUAGE SQL IMMUTABLE;

-- falta score de qualidade das regras: os mais usados nas listagens tidas como de "boas siglas"
-- terão maior score.

Usando nas estatísticas das abreviações conhecidas:

-- full
SELECT count(*) n, 
       count(*) FILTER (WHERE candidatas?abbrev3) as n_bate, 
       count(*) FILTER (WHERE uf='SP') as n_sp, 
       count(*) FILTER (WHERE uf='SP' AND candidatas?abbrev3) as n_sp_bate 
  FROM (
    SELECT name, uf, abbrev3, abbrev.apply_rules(name) as candidatas 
    FROM brcodes_city where abbrev3 is not null
  )  t;

-- not SP:
SELECT count(*) n, 
       count(*) FILTER (WHERE candidatas?abbrev3) as n_bate 
  FROM (
    SELECT name, uf, abbrev3, abbrev.apply_rules(name) as candidatas 
    FROM brcodes_city where abbrev3 is not null AND uf!='SP'
  )  t ;

------ principal:
-- SP:  (depois trocar por =! 'SP')
SELECT t2.rv, count(*) n, count(*) FILTER (WHERE t2.rk=t.abbrev3) n_bate 
  FROM (
    SELECT name, uf, abbrev3, abbrev.apply_rules(name) as candidatas 
    FROM brcodes_city where abbrev3 is not null AND uf='SP'
  )  t, jsonb_each_text(candidatas) t2(rk,rv) 
  WHERE candidatas is not null AND candidatas!='{}'::jsonb 
  GROUP BY 1
  ORDER BY 1
;

Resultado geral:

  n n_bate n_sp
Contagem 4503 887 645
Percentual 20% 14%

Resultados SP

Percentuais relativos ao total de municípios em São Paulo (645).

regra tem tem_perc bate bate_perc bate_percTot
r01 645 100% 88 30% 14%
r02 53 8% 32 11% 5%
r03 219 34% 22 7% 3%
r04 566 88% 153 52% 24%
(total) 371 57% 295 100% 46%

Como na análise subjetiva o conjunto foi considerado de "de qualidade", o valor de 46% do total fazendo uso das quatro regras, passa a ser um valor de referência para boa qualidade.

Conclui-se que as regras mais usadas são r4, r1, r2, r3 nesta ordem. O créscimo de mais uma ou duas regras seria suficiente para amostrar mais de 50% dos municípios.

PS: as médias são apenas indicativos do número de casos em que uma regra pode ser obtida (e sem colisão com regra anterior).

Resultados Anatel não-SP:

Além das regras de formação deve-se acrescentar regras de penalização, tais como "letra ausente no nome" ou "sequência fora de ordem".

total bate bate_percTot
3858 592 15,3%
regra tem tem_perc bate bate_perc bate_percTot
r01 3858 100% 139 23% 4%
r02 432 11% 82 14% 2%
r03 1707 44% 100 17% 3%
r04 3447 89% 271 46% 7%
total 2361 61% 592 100% 15%

O percentual do total de casos que batem é o principal indicador: mostra a baixa qualidade das siglas da Anatel.

O espectro de uso (bate_perc) é o mesmo que SP, sugerindo que esse perfil é independente da perda de qualidade por colisão.