¿Qué tan rápido debo esperar que PostGIS geocodifique direcciones bien formateadas?

17

¿Qué tan rápido debo esperar que PostGIS geocodifique direcciones bien formateadas?

Instalé PostgreSQL 9.3.7 y PostGIS 2.1.7, cargué los datos de la nación y todos los datos de los estados, pero he encontrado que la geocodificación es mucho más lenta de lo que esperaba. ¿Puse mis expectativas demasiado altas? Estoy obteniendo un promedio de 3 códigos geográficos individuales por segundo. Necesito hacer unos 5 millones y no quiero esperar tres semanas para esto.

Esta es una máquina virtual para procesar matrices R gigantes e instalé esta base de datos en el lateral para que la configuración pueda parecer un poco tonta. Si una alteración importante en la configuración de la VM ayudará, puedo alterar la configuración.

Especificaciones de hardware

Memoria: procesadores de 65 GB: 6 lscpume da esto:

# lscpu
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                6
On-line CPU(s) list:   0-5
Thread(s) per core:    1
Core(s) per socket:    1
Socket(s):             6
NUMA node(s):          1
Vendor ID:             GenuineIntel
CPU family:            6
Model:                 58
Stepping:              0
CPU MHz:               2400.000
BogoMIPS:              4800.00
Hypervisor vendor:     VMware
Virtualization type:   full
L1d cache:             32K
L1i cache:             32K
L2 cache:              256K
L3 cache:              30720K
NUMA node0 CPU(s):     0-5

OS es centos, uname -rvda esto:

# uname -rv
2.6.32-504.16.2.el6.x86_64 #1 SMP Wed Apr 22 06:48:29 UTC 2015

Postgresql config

> select version()
"PostgreSQL 9.3.7 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11), 64-bit"
> select PostGIS_Full_version()
POSTGIS="2.1.7 r13414" GEOS="3.4.2-CAPI-1.8.2 r3921" PROJ="Rel. 4.8.0, 6 March 2012" GDAL="GDAL 1.9.2, released 2012/10/08" LIBXML="2.7.6" LIBJSON="UNKNOWN" TOPOLOGY RASTER"

Según las sugerencias anteriores para este tipo de consultas, aumenté shared_buffersel postgresql.confarchivo a aproximadamente 1/4 de RAM disponible y el tamaño efectivo de caché a 1/2 de RAM:

shared_buffers = 16096MB     
effective_cache_size = 31765MB

Tengo installed_missing_indexes()y (después de resolver inserciones duplicadas en algunas tablas) no tuve ningún error.

Ejemplo de codificación geográfica SQL # 1 (lote) ~ el tiempo medio es 2.8 / seg

Estoy siguiendo el ejemplo de http://postgis.net/docs/Geocode.html , que me hace crear una tabla que contiene la dirección para geocodificar, y luego hacer un SQL UPDATE:

UPDATE addresses_to_geocode
              SET  (rating, longitude, latitude,geo) 
              = ( COALESCE((g.geom).rating,-1),
              ST_X((g.geom).geomout)::numeric(8,5), 
              ST_Y((g.geom).geomout)::numeric(8,5),
              geo )
              FROM (SELECT "PatientId" as PatientId
              FROM addresses_to_geocode 
              WHERE "rating" IS NULL ORDER BY PatientId LIMIT 1000) As a
              LEFT JOIN (SELECT "PatientId" as PatientId, (geocode("Address",1)) As geom
              FROM addresses_to_geocode As ag
              WHERE ag.rating IS NULL ORDER BY PatientId LIMIT 1000) As g ON a.PatientId = g.PatientId
              WHERE a.PatientId = addresses_to_geocode."PatientId";

Estoy usando un tamaño de lote de 1000 arriba y regresa en 337.70 segundos. Es un poco más lento para lotes más pequeños.

Ejemplo de SQL de codificación geográfica # 2 (fila por fila) ~ el tiempo medio es 1.2 / seg

Cuando busco en mis direcciones haciendo los códigos geográficos uno a la vez con una declaración que se ve así (por cierto, el ejemplo a continuación tomó 4.14 segundos),

SELECT g.rating, ST_X(g.geomout) As lon, ST_Y(g.geomout) As lat, 
    (addy).address As stno, (addy).streetname As street, 
    (addy).streettypeabbrev As styp, (addy).location As city, 
    (addy).stateabbrev As st,(addy).zip 
FROM geocode('6433 DROMOLAND Cir NW, MASSILLON, OH 44646',1) As g;

es un poco más lento (2.5x por registro), pero puedo ver la distribución de los tiempos de consulta y ver que es una minoría de consultas largas las que lo ralentizan más (solo los primeros 2600 de 5 millones tienen tiempos de búsqueda). Es decir, el 10% superior está tomando un promedio de aproximadamente 100 ms, el 10% inferior promedio 3.69 segundos, mientras que la media es 754 ms y la mediana es 340 ms.

# Just some interaction with the data in R
> range(lookupTimes[1:2600])
[1]  0.00 11.54
> median(lookupTimes[1:2600])
[1] 0.34
> mean(lookupTimes[1:2600])
[1] 0.7541808
> mean(sort(lookupTimes[1:2600])[1:260])
[1] 0.09984615
> mean(sort(lookupTimes[1:2600],decreasing=TRUE)[1:260])
[1] 3.691269
> hist(lookupTimes[1:2600]

Tiempos de geocodificación para las primeras 2600 filas

Otros pensamientos

Si no puedo obtener un aumento de orden de magnitud en el rendimiento, pensé que al menos podría hacer una suposición educada sobre la predicción de tiempos lentos de geocodificación, pero no es obvio para mí por qué las direcciones más lentas parecen estar tardando mucho más. Estoy ejecutando la dirección original a través de un paso de normalización personalizado para asegurarme de que esté bien formateado antes de que la geocode()función lo obtenga:

sql=paste0("select pprint_addy(normalize_address('",myAddress,"'))")

donde myAddresses una [Address], [City], [ST] [Zip]cadena compilada de una tabla de direcciones de usuario de una base de datos no postgresql.

Intenté (fallé) instalar la pagc_normalize_addressextensión, pero no está claro que esto traerá el tipo de mejora que estoy buscando. Editado para agregar información de monitoreo según la sugerencia

Actuación

Una CPU está vinculada: [editar, solo un procesador por consulta, por lo que tengo 5 CPU no utilizadas]

top - 14:10:26 up 1 day,  3:11,  4 users,  load average: 1.02, 1.01, 0.93
Tasks: 219 total,   2 running, 217 sleeping,   0 stopped,   0 zombie
Cpu(s): 15.4%us,  1.5%sy,  0.0%ni, 83.1%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:  65056588k total, 64613476k used,   443112k free,    97096k buffers
Swap: 262139900k total,    77164k used, 262062736k free, 62745284k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 3130 postgres  20   0 16.3g 8.8g 8.7g R 99.7 14.2 170:14.06 postmaster
11139 aolsson   20   0 15140 1316  932 R  0.3  0.0   0:07.78 top
11675 aolsson   20   0  135m 1836 1504 S  0.3  0.0   0:00.01 wget
    1 root      20   0 19364 1064  884 S  0.0  0.0   0:01.84 init
    2 root      20   0     0    0    0 S  0.0  0.0   0:00.06 kthreadd

Muestra de actividad de disco en la partición de datos mientras un proceso está vinculado al 100%: [editar: solo un procesador en uso por esta consulta]

# dstat -tdD dm-3 1
----system---- --dsk/dm-3-
  date/time   | read  writ
12-06 14:06:36|1818k 3632k
12-06 14:06:37|   0     0
12-06 14:06:38|   0     0
12-06 14:06:39|   0     0
12-06 14:06:40|   0    40k
12-06 14:06:41|   0     0
12-06 14:06:42|   0     0
12-06 14:06:43|   0  8192B
12-06 14:06:44|   0  8192B
12-06 14:06:45| 120k   60k
12-06 14:06:46|   0     0
12-06 14:06:47|   0     0
12-06 14:06:48|   0     0
12-06 14:06:49|   0     0
12-06 14:06:50|   0    28k
12-06 14:06:51|   0    96k
12-06 14:06:52|   0     0
12-06 14:06:53|   0     0
12-06 14:06:54|   0     0 ^C

Analiza ese SQL

Esto es de EXPLAIN ANALYZEesa consulta:

"Update on addresses_to_geocode  (cost=1.30..8390.04 rows=1000 width=272) (actual time=363608.219..363608.219 rows=0 loops=1)"
"  ->  Merge Left Join  (cost=1.30..8390.04 rows=1000 width=272) (actual time=110.934..324648.385 rows=1000 loops=1)"
"        Merge Cond: (a.patientid = g.patientid)"
"        ->  Nested Loop  (cost=0.86..8336.82 rows=1000 width=184) (actual time=10.676..34.241 rows=1000 loops=1)"
"              ->  Subquery Scan on a  (cost=0.43..54.32 rows=1000 width=32) (actual time=10.664..18.779 rows=1000 loops=1)"
"                    ->  Limit  (cost=0.43..44.32 rows=1000 width=4) (actual time=10.658..17.478 rows=1000 loops=1)"
"                          ->  Index Scan using "addresses_to_geocode_PatientId_idx" on addresses_to_geocode addresses_to_geocode_1  (cost=0.43..195279.22 rows=4449758 width=4) (actual time=10.657..17.021 rows=1000 loops=1)"
"                                Filter: (rating IS NULL)"
"                                Rows Removed by Filter: 24110"
"              ->  Index Scan using "addresses_to_geocode_PatientId_idx" on addresses_to_geocode  (cost=0.43..8.27 rows=1 width=152) (actual time=0.010..0.013 rows=1 loops=1000)"
"                    Index Cond: ("PatientId" = a.patientid)"
"        ->  Materialize  (cost=0.43..18.22 rows=1000 width=96) (actual time=100.233..324594.558 rows=943 loops=1)"
"              ->  Subquery Scan on g  (cost=0.43..15.72 rows=1000 width=96) (actual time=100.230..324593.435 rows=943 loops=1)"
"                    ->  Limit  (cost=0.43..5.72 rows=1000 width=42) (actual time=100.225..324591.603 rows=943 loops=1)"
"                          ->  Index Scan using "addresses_to_geocode_PatientId_idx" on addresses_to_geocode ag  (cost=0.43..23534259.93 rows=4449758000 width=42) (actual time=100.225..324591.146 rows=943 loops=1)"
"                                Filter: (rating IS NULL)"
"                                Rows Removed by Filter: 24110"
"Total runtime: 363608.316 ms"

Vea un mejor desglose en http://explain.depesz.com/s/vogS

aaryno
fuente
1
¿Qué hace la máquina cuando ejecuta las consultas? ¿Se bloquea en IO o es el cuello de botella en otro lugar?
til_b
1
¿Cuántos estados has cargado? Generalmente obtengo entre 30 ms y 150 ms por dirección en un cuadro de Windows de 64 bits con 4 a 8 GB de RAM. Por lo general, aunque solo estoy trabajando con 1 o 2 estados. No he hecho un punto de referencia sobre el impacto de más estados en el rendimiento.
LR1234567
@ LR1234567 50 estados
aaryno
1
@til_b CPU está vinculada al 99.7%
aaryno
Parece que solo vamos a esperar un par de semanas para terminar esto, ya que es una cosa de una sola vez y tendremos mucho jugo una vez que esté hecho para mantener el ritmo de carga de 100 direcciones / día de ejecución estamos experimentando. Mantendré esto abierto hasta que terminemos en caso de que surja algo realmente convincente que nos permita sortear nuestras CPU vinculadas.
aaryno

Respuestas:

7

He pasado mucho tiempo experimentando con esto, creo que es mejor publicar por separado ya que son desde un ángulo diferente.

Este es realmente un tema complejo, vea más detalles en la publicación de mi blog sobre la configuración del servidor de geocodificación y el script que utilicé . Aquí hay algunos breves resúmenes:

Un servidor con datos de solo 2 estados siempre es más rápido que un servidor cargado con los datos de 50 estados.

Verifiqué esto con mi PC doméstica en diferentes momentos y dos servidores diferentes de Amazon AWS.

Mi servidor de nivel gratuito AWS con datos de 2 estados tiene solo 1G de RAM, pero tiene un rendimiento constante de 43 ~ 59 ms para datos con 1000 registros y 45,000 registros.

Utilicé exactamente el mismo procedimiento de configuración para un servidor AWS 8G RAM con todos los estados cargados, exactamente el mismo script y datos, y el rendimiento se redujo a 80 ~ 105 ms.

Mi teoría es que cuando el geocodificador no puede coincidir exactamente con la dirección, comenzó a ampliar el rango de búsqueda e ignorar alguna parte, como el código postal o la ciudad. Es por eso que el documento de geocodificación se jacta de que puede recolonizar la dirección con un código postal incorrecto, aunque tomó 3000 ms.

Con solo 2 datos de estados cargados, el servidor tomará mucho menos tiempo en búsquedas infructuosas o una coincidencia con puntaje muy bajo, ya que solo puede buscar en 2 estados.

Intenté limitar esto estableciendo el restrict_regionparámetro en los multipolígonos de estado en la función de geocodificación, con la esperanza de evitar la búsqueda infructuosa, ya que estoy bastante seguro de que la mayoría de las direcciones tienen el estado correcto. Compare estas dos versiones:

  select geocode('501 Fairmount DR , Annapolis, MD 20137',1); 
  select geocode('501 Fairmount DR , Annapolis, MD 20137', 1, the_geom) from tiger.state where statefp = '24';

La única diferencia que hace la segunda versión es que, normalmente, si ejecuto la misma consulta de inmediato, será mucho más rápido porque los datos relacionados se almacenaron en caché, pero la segunda versión deshabilitó este efecto.

Entonces, restrict_regionno funciona como deseaba, tal vez solo se usó para filtrar el resultado de múltiples aciertos, no para limitar los rangos de búsqueda.

Puede ajustar un poco su configuración de postgre.

El sospechoso habitual de instalar índices faltantes, el análisis de vacío no hizo ninguna diferencia para mí, porque la secuencia de comandos de descarga ya ha realizado el mantenimiento necesario, a menos que se haya equivocado con ellos.

Sin embargo, establecer postgre conf de acuerdo con esta publicación ayudó. Mi servidor de escala completa con 50 estados tenía 320 ms con la configuración predeterminada para algunos datos con peor forma, mejoró a 185 ms con 2G shared_buffer, 5G cache, y fue a 100 ms más con la mayoría de las configuraciones ajustadas de acuerdo con esa publicación.

Esto es más relevante para los postgis y su configuración parece ser similar.

El tamaño del lote de cada confirmación no importó mucho para mi caso. La documentación de geocodificación utilizaba un tamaño de lote 3. Experimenté valores desde 1, 3, 5 hasta 10. No encontré ninguna diferencia significativa con esto. Con un tamaño de lote más pequeño, realiza más confirmaciones y actualizaciones, pero creo que el cuello de botella real no está aquí. En realidad estoy usando el tamaño de lote 1 ahora. Como siempre hay alguna dirección mal formada inesperada que causará una excepción, estableceré todo el lote con error como ignorado y procederé para las filas restantes. Con el tamaño de lote 1 no necesito procesar la tabla la segunda vez para geocodificar los posibles registros buenos en el lote marcado como ignorado.

Por supuesto, esto depende de cómo funcione su script por lotes. Publicaré mi script con más detalles más adelante.

Puede intentar utilizar la dirección normalizada para filtrar la dirección incorrecta si se ajusta a su uso. Vi que alguien mencionó esto en alguna parte, pero no estaba seguro de cómo funciona, ya que la función normalizar solo funciona en formato, realmente no puede decir qué dirección es inválida.

Más tarde, me di cuenta de que si la dirección está obviamente en mal estado y desea omitirla, esto podría ayudar. Por ejemplo, tengo muchas direcciones que faltan nombres de calles o incluso nombres de calles. Normalizar todas las direcciones primero será relativamente rápido, luego puede filtrar la dirección incorrecta obvia para usted y luego omitirlas. Sin embargo, esto no se ajustaba a mi uso, ya que una dirección sin número de calle o incluso nombre de la calle aún podría mapearse a la calle o la ciudad, y esa información todavía es útil para mí.

Y la mayoría de las direcciones que no se pueden geocodificar en mi caso en realidad tienen todos los campos, solo que no hay coincidencia en la base de datos. No puede filtrar estas direcciones simplemente normalizándolas.

EDITAR Para obtener más detalles, consulte la publicación de mi blog sobre la configuración del servidor de geocodificación y el script que utilicé .

EDITAR 2 He terminado de geocodificar 2 millones de direcciones e hice muchas limpiezas de direcciones en función del resultado de geocodificación. Con una entrada mejor limpia, el siguiente trabajo por lotes se ejecuta mucho más rápido. Por limpio quiero decir que algunas direcciones son obviamente incorrectas y deben eliminarse, o tener contenido inesperado para que el geocodificador cause problemas en la geocodificación. Mi teoría es: eliminar las direcciones incorrectas puede evitar estropear la memoria caché, lo que mejora significativamente el rendimiento de las direcciones correctas.

Separé la entrada según el estado para asegurarme de que cada trabajo pueda tener todos los datos necesarios para la geocodificación en caché en la RAM. Sin embargo, cada dirección incorrecta en el trabajo hace que el geocodificador busque en más estados, lo que podría dañar el caché.

dracodoc
fuente
Gran respuesta En mi caja, como sucede, el filtrado por estado acelera la coincidencia en alrededor de un factor de 50 (!) Pero sospecho que puedo tener problemas con el índice.
ako
2
  1. De acuerdo con este hilo de discusión , se supone que debe utilizar el mismo procedimiento de normalización para procesar los datos de Tiger y su dirección de entrada. Dado que los datos de Tiger se han procesado con el normalizador incorporado, es mejor usar solo el normalizador incorporado. Incluso si consiguió que pagc_normalizer funcionara, es posible que no lo ayude si no lo usa para actualizar los datos de Tiger.

    Dicho esto, creo que geocode () llamará al normalizador de todos modos, por lo que normalizar la dirección antes de la geocodificación puede no ser realmente útil. Un posible uso del normalizador podría ser comparar la dirección normalizada y la dirección devuelta por geocode (). Con ambos normalizados, podría ser más fácil encontrar el resultado de geocodificación incorrecto.

    Si puede filtrar la dirección incorrecta del geocódigo por normalizador, eso realmente ayudará. Sin embargo, no veo que el normalizador tenga nada como un puntaje o calificación de partido.

  2. El mismo hilo de discusión también mencionó un cambio de depuración geocode_addresspara mostrar más información. El nodo geocode_addressnecesita una entrada de dirección normalizada.

  3. Geocoder es rápido para la coincidencia exacta, pero toma mucho más tiempo para casos difíciles. Descubrí que hay un parámetro restrict_regiony pensé que tal vez limitaría la búsqueda infructuosa si configuro el límite como estado, ya que estoy bastante seguro de en qué estado estará. Resultó que configurarlo en un estado incorrecto no detuvo el geocodificación para obtener la dirección correcta, aunque lleva algún tiempo.

    Entonces, tal vez el geocodificador buscará en todos los lugares posibles si la primera búsqueda exacta no tiene coincidencia. Esto hace que pueda procesar la entrada con algunos errores, pero también hace que la búsqueda sea muy lenta.

    Creo que es bueno que un servicio interactivo acepte entradas con errores, pero a veces es posible que deseemos renunciar a un pequeño conjunto de direcciones incorrectas para tener un mejor rendimiento en la geocodificación por lotes.

dracodoc
fuente
¿Cuál fue el impacto del restrict_regiontiempo cuando configuró el estado correcto? Además, del hilo postgis-users al que vinculaste anteriormente, mencionan específicamente tener problemas con direcciones como las 1020 Highway 20que encontré también.
aaryno
Establecer el estado correcto probablemente no mejorará, ya que si la dirección está bien formateada, el geocodificador puede obtener el estado correcto de todos modos.
dracodoc
1

Voy a publicar esta respuesta, pero espero que otro colaborador ayude a desglosar lo siguiente, que creo que pintará una imagen más coherente:

¿Cuál es el impacto del número de estados cargados en la geocodificación? Tengo los 50 y veo un rendimiento mucho más bajo que @ LR1234567 (es decir, 8 veces por vez geocode).

¿Cuál es el método más eficiente para la geocodificación masiva? Estoy ejecutando un proceso en serie, ejecutando lotes de 100 repetidamente hasta que termine toda la carga. Sería preferible un enfoque multiproceso, pero ¿qué enfoques se recomiendan?

¿Cuál es el impacto de la virtualización en la geocodificación PostgreSQL? Supongo que el 10% se basa en otras publicaciones, pero tengo poca confianza en esa respuesta

Ahora mi respuesta, que es solo una anécdota:

Lo mejor que obtengo (basado en una sola conexión) es un promedio de 208 ms por geocode. Esto se mide seleccionando direcciones al azar de mi conjunto de datos, que se extiende a través de los EE. UU. Incluye algunos datos sucios, pero los correos electrónicos más antiguos geocodeno parecen ser malos de manera obvia.

La esencia de esto es que parezco estar vinculado a la CPU y que una sola consulta está vinculada a un único procesador. Puedo paralelizar esto al tener múltiples conexiones ejecutándose UPDATEen segmentos complementarios de la addresses_to_geocodetabla en teoría. Mientras tanto, voy geocodea tomar un promedio de 208 ms en el conjunto de datos a nivel nacional. La distribución está sesgada tanto en términos de dónde están la mayoría de mis direcciones como en términos de cuánto tiempo están tomando (por ejemplo, vea el histograma anterior) y la tabla a continuación.

Mi mejor enfoque hasta ahora es hacerlo en lotes de 10000, con alguna mejora estimable de hacer más por lote. Para lotes de 100 obtenía unos 251 ms, con 10000 obtenía 208 ms.

UPDATE addresses_to_geocode 
SET (rating, longitude, latitude, geo) = 
   (COALESCE((g.geom).rating,-1), 
            ST_X((g.geom).geomout)::numeric(8,5),   
            ST_Y((g.geom).geomout)::numeric(8,5), 
            geo) 
   FROM (
       SELECT "PatientId" as PatientId 
       FROM addresses_to_geocode  
       WHERE "rating" IS NULL 
       ORDER BY PatientId LIMIT 100) As a 
   LEFT JOIN (
       SELECT "PatientId" as PatientId, (geocode("Address",1)) As geom 
       FROM addresses_to_geocode As ag 
       WHERE ag.rating IS NULL 
       ORDER BY PatientId LIMIT 100) As g 
   ON a.PatientId = g.PatientId 
   WHERE a.PatientId = addresses_to_geocode."PatientId";

Tengo que citar nombres de campo debido a cómo RPostgreSQL crea las tablas con dbWriteTable

Eso es aproximadamente 4 veces más rápido que si los hago un registro a la vez. Cuando los hago uno a la vez, puedo obtener un desglose por estado (ver más abajo). Hice esto para verificar y ver si uno o más de los estados TIGER tenían una carga o índice incorrectos, lo que esperaba que tuviera un bajo geocoderendimiento en el estado. Obviamente tengo algunos datos incorrectos (¡algunas direcciones son incluso direcciones de correo electrónico!), Pero la mayoría de ellos están bien formateados. Como dije antes, algunas de las consultas más largas no tienen deficiencias obvias en su formato. A continuación se muestra una tabla del número, el tiempo mínimo de consulta, el tiempo medio de consulta y el tiempo máximo de consulta para estados de 3000-algunas direcciones aleatorias de mi conjunto de datos:

       state   n  min      mean   max
1          .   1 0.00 0.0000000  0.00
12        DC   6 0.07 0.0900000  0.10
9  CHIHUAHUA   1 0.16 0.1600000  0.16
2         00   1 0.18 0.1800000  0.18
6         AR   1 0.37 0.3700000  0.37
27        MT  17 0.14 0.4229412  1.01
14        GA  37 0.22 0.4340541  2.78
10        CO   1 0.54 0.5400000  0.54
16        IL 390 0.16 0.5448974  3.75
8         CA 251 0.17 0.5546614  3.58
5         AL   4 0.13 0.5575000  0.86
18        KS   3 0.43 0.5966667  0.75
23        ME 121 0.14 0.6266116  7.88
35        SC 390 0.14 0.6516923  6.88
24        MI  62 0.12 0.6524194  3.36
40        WA   3 0.23 0.7500000  1.41
32        OK 145 0.17 0.7538621  5.84
20        LA   1 0.76 0.7600000  0.76
31        OH 551 0.00 0.7623775 10.27
17        IN 108 0.19 0.7864815  3.64
43      <NA>  89 0.00 0.8152809  4.98
15        IA   1 0.82 0.8200000  0.82
30        NY 227 0.19 0.8227753 28.47
19        KY   3 0.56 0.8333333  1.36
36        TN 333 0.11 0.8566667  6.45
28        NC 129 0.24 0.8843411  4.07
13        FL  70 0.28 0.9131429  4.65
7         AZ 101 0.20 0.9498020  6.33
34        PA  56 0.14 0.9594643  3.61
29        NJ   1 1.03 1.0300000  1.03
33        OR 101 0.24 1.0966337 14.89
26        MS  28 0.25 1.1503571 11.89
3          9   6 0.58 1.2133333  1.93
4         AK   1 1.25 1.2500000  1.25
22        MD   9 0.50 1.3055556  4.17
25        MO  22 0.31 1.3381818  4.20
42        WY   1 1.38 1.3800000  1.38
38        VA 127 0.20 1.3873228  5.69
37        TX   4 0.53 1.4800000  3.28
21        MA   4 0.47 1.5725000  3.63
11        CT   5 0.38 1.6760000  4.68
39        VT   1 2.25 2.2500000  2.25
41        WI   2 2.27 2.2850000  2.30
aaryno
fuente