This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
medicacion2021 [2021/03/27 12:25] osotolongo [Limpiando y Haciendo las reglas] |
medicacion2021 [2021/04/03 10:42] osotolongo [tl;dr] |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== Informe de medicacion ====== | ====== Informe de medicacion ====== | ||
+ | Ver programas en github: https:// | ||
+ | |||
+ | ===== tl;dr ===== | ||
+ | |||
+ | El formato de la base de datos de input ha de ser: | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | |||
+ | y se ejecutan dos scripts consecutivos, | ||
+ | |||
+ | <code bash> | ||
+ | $ ./parse.pl medicamentos.db | ||
+ | $ ./ | ||
+ | </ | ||
+ | |||
+ | Y los resultados quedan en el archivo // | ||
===== Antes de empezar ===== | ===== Antes de empezar ===== | ||
Line 23: | Line 40: | ||
Asi que hacemos, | Asi que hacemos, | ||
<code bash> | <code bash> | ||
- | [osotolongo@brick03 medicacion]$ awk -F";" | + | [osotolongo@brick03 medicacion]$ awk -F";" |
</ | </ | ||
Line 29: | Line 46: | ||
<code bash> | <code bash> | ||
- | [osotolongo@brick03 medica]$ head anamnesi.db | + | [osotolongo@brick03 medica]$ head anamnesi.db |
Interno; | Interno; | ||
19960001; | 19960001; | ||
- | 19960003; | + | 19960003; |
- | 19960004; | + | 19960004; |
- | 19960006; | + | 19960006; |
19960008; | 19960008; | ||
19960009; | 19960009; | ||
Line 43: | Line 60: | ||
---- | ---- | ||
- | **Ojo aqui:** El comando //awk// selecciona lsolo las columnas que necesitamos. Esto incluye el //ID//, la fecha de cada visita y el texto libre que se scribe | + | **Ojo aqui:** El comando //awk// selecciona lsolo las columnas que necesitamos. Esto incluye el //ID//, la fecha de cada visita y el texto libre que se escribe |
---- | ---- | ||
Line 65: | Line 82: | ||
<code bash> | <code bash> | ||
- | [osotolongo@brick03 medicacion]$ awk -F";" | + | [osotolongo@brick03 medicacion]$ awk -F";" |
</ | </ | ||
Line 71: | Line 88: | ||
<code bash> | <code bash> | ||
- | [osotolongo@brick03 medica]$ head seguimientos.db | + | [osotolongo@brick03 medica]$ head seguimientos.db |
Interno; | Interno; | ||
19960014; | 19960014; | ||
- | 19960044; | + | 19960044; |
19960068; | 19960068; | ||
- | 19960171; | + | 19960171; |
- | 19960171; | + | 19960171; |
- | 19960171; | + | 19960171; |
- | 19960171; | + | 19960171; |
- | 19960171; | + | 19960171; |
- | 19960171; | + | 19960171; |
</ | </ | ||
Line 94: | Line 111: | ||
===== Limpiando y Haciendo las reglas ===== | ===== Limpiando y Haciendo las reglas ===== | ||
+ | |||
+ | ==== Limpieza ==== | ||
La manera mas sencilla de hacer el parser es en Perl por varias razones, | La manera mas sencilla de hacer el parser es en Perl por varias razones, | ||
Line 102: | Line 121: | ||
- Es muy sencillo insertar un envio de email al final del script | - Es muy sencillo insertar un envio de email al final del script | ||
- | **No medicamenteos:** Como la mayor parte de la limpieza la voy a hacer con expresiones regulares, voy a tomar el archivo // | + | **No medicamentos:** Como la mayor parte de la limpieza la voy a hacer con expresiones regulares, voy a tomar el archivo // |
<code bash> | <code bash> | ||
Line 108: | Line 127: | ||
</ | </ | ||
- | Ahora, este archivo lo necesito de input para el parser. Lo voy a convertir en un //array// y despues solo tengo que quitar estas palabras cada vez que las encuentre. O algo parecido. Asi que dentro del codigo | + | Ahora, este archivo lo necesito de input para el parser. Lo voy a convertir en un //array// y despues solo tengo que quitar estas palabras cada vez que las encuentre. O algo parecido. Asi que dentro del codigo |
<code perl> | <code perl> | ||
Line 126: | Line 145: | ||
</ | </ | ||
- | y el ultimo campo lo metemos en un //array//, tomando los espacios como separador de valores, | + | y al ultimo campo le cambiamos acentos, dieresis, etc por la letra simple y le quitamos los caracteres no alfanumericos y lo metemos en un //array//, tomando los espacios como separador de valores, |
<code perl> | <code perl> | ||
+ | $free = unidecode($free); | ||
+ | $free =~ s/\W/ /g; | ||
my @afree = split / /, $free; | my @afree = split / /, $free; | ||
</ | </ | ||
A cada elemento del array le hacemos una limpieza en este orden, | A cada elemento del array le hacemos una limpieza en este orden, | ||
- | - le quitamos todos lo caracteres no alfanumericos, | + | |
- si empieza con numeros nos quedamos con lo que haya despues de los numeros, | - si empieza con numeros nos quedamos con lo que haya despues de los numeros, | ||
- si tiene numeros dentro, nos quedamos con lo que haya antes de los numeros, | - si tiene numeros dentro, nos quedamos con lo que haya antes de los numeros, | ||
Line 145: | Line 166: | ||
my @nonfree; | my @nonfree; | ||
foreach my $token (@afree){ | foreach my $token (@afree){ | ||
- | $token =~ s/\W//g; | ||
if( $token =~ /^\d+.*$/) { $token =~ s/^\d+//g;} | if( $token =~ /^\d+.*$/) { $token =~ s/^\d+//g;} | ||
if( $token =~ / | if( $token =~ / | ||
Line 167: | Line 187: | ||
</ | </ | ||
- | lo que me ha queado | + | lo que me ha quedado |
<code perl> | <code perl> | ||
Line 178: | Line 198: | ||
} | } | ||
</ | </ | ||
+ | |||
+ | ==== Reglas ==== | ||
+ | |||
+ | Las palabras que se repiten mas de **X** veces van a ser las cabeceras de las reglas. Voy a seleccionarlas, | ||
+ | |||
+ | <code perl> | ||
+ | my $repeat = 12; | ||
+ | foreach my $med (sort keys %meds){ | ||
+ | if ($meds{$med} > $repeat){ push @medkw, $med; } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Y ahora, voy a calcular la distancia de cada palabra a las cabeceras de reglas y la voy a poner bajo la mas cercana. Las palabras que no entren bajo ninguna regla las pondre en un //array// aparte. | ||
+ | |||
+ | <code perl> | ||
+ | my $dtresh = 5; | ||
+ | my %medgroups; | ||
+ | my @alonemeds; | ||
+ | foreach my $medword (sort keys %meds){ | ||
+ | my $mindist = 1000; | ||
+ | my $keyguide = ""; | ||
+ | foreach my $medkey (@medkw){ | ||
+ | my $dist = distance($medword, | ||
+ | if ((defined $dist) && ($dist < $mindist)) { | ||
+ | $mindist = $dist; | ||
+ | $keyguide = $medkey; | ||
+ | } | ||
+ | } | ||
+ | if ($keyguide) { | ||
+ | push @{$medgroups{$keyguide}}, | ||
+ | }else{ | ||
+ | push @alonemeds, $medword; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Y ahora guardo todo esto a disco, | ||
+ | |||
+ | <code perl> | ||
+ | my $ofile = ' | ||
+ | my $obfile = ' | ||
+ | open ODF, "> | ||
+ | foreach my $mgroup (sort keys %medgroups){ | ||
+ | print ODF " | ||
+ | } | ||
+ | close ODF; | ||
+ | open ODF, "> | ||
+ | foreach my $med (@alonemeds){ | ||
+ | print ODF " | ||
+ | } | ||
+ | close ODF; | ||
+ | </ | ||
+ | |||
+ | **Ahora, las reglas han de revisarse y editarse a mano. Esto no hay manera de evitarlo pero solo ha de hacerse una vez. La proxima vez que se haga el analisis se usara el mismo archivo de reglas.** | ||
+ | |||
+ | ===== Reglas de CIMA ===== | ||
+ | |||
+ | https:// | ||
+ | |||
+ | Me bajo la lista de medicamentos comercializados en España y la convierto a CSV. Luego hago esto: | ||
+ | |||
+ | <code bash> | ||
+ | [osotolongo@brick03 medicaion_dev]$ awk -F";" | ||
+ | </ | ||
+ | |||
+ | O si quiero tener los principios activos, | ||
+ | |||
+ | <code bash> | ||
+ | [osotolongo@brick03 medicaion_dev]$ awk -F";" | ||
+ | [osotolongo@brick03 medicaion_dev]$ sed ' | ||
+ | </ | ||
+ | |||
+ | |||
+ | ===== Aplicando las reglas ===== | ||
+ | |||
+ | Aqui he de leer tres cosas antes que nada. Primero, las reglas que hemos construido. Estas las voy a asignar a un hash de arrays, | ||
+ | |||
+ | <code perl> | ||
+ | my %wrules; | ||
+ | open RDF, "< | ||
+ | while (< | ||
+ | (my $rkey, my $rvalue) = /^(\w*): (.*)$/; | ||
+ | chomp $rvalue; | ||
+ | my @lpat = split /\|/, $rvalue; | ||
+ | %{$wrules{$rkey}} = map {$_ => 1} @lpat unless !$rkey; | ||
+ | } | ||
+ | close RDF; | ||
+ | |||
+ | </ | ||
+ | |||
+ | Luego, la lista de principios activos de [[https:// | ||
+ | |||
+ | <code perl> | ||
+ | my %cima; | ||
+ | open ADF, "< | ||
+ | while (< | ||
+ | my ($med, $pal, $pan) = / | ||
+ | my @pas = split /, /, $pal; | ||
+ | for (@pas) {s/ | ||
+ | $cima{$med} = [ @pas ]; | ||
+ | } | ||
+ | close ADF; | ||
+ | </ | ||
+ | |||
+ | |||
+ | Ahora cargo base de datos ya revisada, que si todo va bien sera el output del script anterior. | ||
+ | |||
+ | <code perl> | ||
+ | my $dbfile = ' | ||
+ | |||
+ | my %visits; | ||
+ | my %meds; | ||
+ | open IDF, "< | ||
+ | while(< | ||
+ | my ($pid, $vid, $ldata) = / | ||
+ | </ | ||
+ | |||
+ | Pero voy a ir llenando el hash de output (// %visits //) a medida que leo. Primero meto toda la medicacion de la visita en un array, | ||
+ | |||
+ | <code perl> | ||
+ | if($ldata){ | ||
+ | my @mlist = split /,/, $ldata; | ||
+ | </ | ||
+ | |||
+ | y ahora, para cada elemento de este array, recorro las reglas, buscando si este termino existe dentro de alguna regla. | ||
+ | |||
+ | <code perl> | ||
+ | foreach my $med (@mlist){ | ||
+ | if ($med){ | ||
+ | foreach my $rkey (sort keys %wrules){ | ||
+ | if(exists($wrules{$rkey}{$med})){ | ||
+ | foreach my $pa (@{$cima{$rkey}}){ | ||
+ | $meds{$pa} = 1; | ||
+ | $visits{$pid}{$vid}{$pa} = 1; | ||
+ | } | ||
+ | last; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Cuando encuentro este elemento en las reglas, marco tods los ID de principios activos correspondientes a este medicamento como existentes en esa visita y ademas los sumo a las variables que he de escribir. Si no se encuentra este termino, se concluye que no es ningun medicamento (pues no esta en ninguna regla) y no se hace nada. | ||
+ | |||
+ | Una vez llenados los medicamentos de cada visita hemos de escribirlo en forma matricial. Primero escribimos los headers, | ||
+ | |||
+ | <code perl> | ||
+ | open ODF, "> | ||
+ | print ODF " | ||
+ | foreach my $med (sort keys %meds){ | ||
+ | print ODF ", | ||
+ | } | ||
+ | print ODF " | ||
+ | </ | ||
+ | |||
+ | Y ahora llenamos los valores correspondientes a cada visita con un 1, caso de existir y un 0 en caso contrario. | ||
+ | |||
+ | <code perl> | ||
+ | foreach my $pid (sort keys %visits){ | ||
+ | foreach my $vid (sort keys %{$visits{$pid}}){ | ||
+ | print ODF " | ||
+ | foreach my $med (sort keys %meds){ | ||
+ | if(exists($visits{$pid}{$vid}{$med})){ | ||
+ | print ODF ", | ||
+ | }else{ | ||
+ | print ODF ", | ||
+ | } | ||
+ | } | ||
+ | print ODF " | ||
+ | } | ||
+ | } | ||
+ | close ODF; | ||
+ | </ | ||
+ | |||
+ | **TADA!** | ||
+ |