À quelle vitesse devrais-je m'attendre à ce que PostGIS géocode des adresses bien formatées?

17

À quelle vitesse devrais-je m'attendre à ce que PostGIS géocode des adresses bien formatées?

J'ai installé PostgreSQL 9.3.7 et PostGIS 2.1.7, chargé les données nationales et toutes les données des États, mais j'ai trouvé que le géocodage était beaucoup plus lent que prévu. Ai-je placé mes attentes trop haut? J'obtiens en moyenne 3 géocodages individuels par seconde. Je dois en faire environ 5 millions et je ne veux pas attendre trois semaines pour cela.

Il s'agit d'une machine virtuelle pour le traitement des matrices R géantes et j'ai installé cette base de données sur le côté afin que la configuration puisse paraître un peu maladroite. Si une modification majeure de la configuration de la VM peut aider, je peux modifier la configuration.

Spécifications matérielles

Mémoire: processeurs de 65 Go: 6 lscpume donne ceci:

# 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 is centos, uname -rvdonne ceci:

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

Configuration postgresql

> 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"

Sur la base des suggestions précédentes concernant ces types de requêtes, j'ai augmenté shared_buffersle postgresql.conffichier à environ 1/4 de la RAM disponible et la taille du cache efficace à la moitié de la RAM:

shared_buffers = 16096MB     
effective_cache_size = 31765MB

J'ai installed_missing_indexes()et (après avoir résolu les insertions en double dans certaines tables) je n'ai eu aucune erreur.

Exemple de géocodage SQL # 1 (batch) ~ le temps moyen est de 2,8 / sec

Je suis l'exemple de http://postgis.net/docs/Geocode.html , qui m'a fait créer une table contenant l'adresse à géocoder, puis faire 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";

J'utilise une taille de lot de 1000 au-dessus et elle revient en 337,70 secondes. C'est un peu plus lent pour les petits lots.

Exemple de géocodage SQL # 2 (ligne par ligne) ~ le temps moyen est de 1,2 / sec

Lorsque je fouille dans mes adresses en faisant les géocodes un à la fois avec une déclaration qui ressemble à ceci (btw, l'exemple ci-dessous a pris 4,14 secondes),

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;

c'est un peu plus lent (2,5 fois par enregistrement) mais je peux regarder la distribution des temps de requête et voir que c'est une minorité de longues requêtes qui ralentissent le plus (seuls les premiers 2600 sur 5 millions ont des temps de recherche). Autrement dit, les 10% supérieurs prennent en moyenne environ 100 ms, les 10% inférieurs en moyenne 3,69 secondes, tandis que la moyenne est de 754 ms et la médiane est de 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]

Temps de géocodage pour les 2600 premières lignes

D'autres pensées

Si je ne peux pas obtenir un ordre de grandeur d'augmentation des performances, je pensais que je pourrais au moins faire une supposition éclairée sur la prévision des temps de géocodage lents, mais il n'est pas évident pour moi pourquoi les adresses plus lentes semblent prendre beaucoup plus de temps. J'exécute l'adresse d'origine via une étape de normalisation personnalisée pour m'assurer qu'elle est bien formatée avant que la geocode()fonction ne l'obtienne:

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

myAddressest une [Address], [City], [ST] [Zip]chaîne compilée à partir d'une table d'adresses utilisateur à partir d'une base de données non postgresql.

J'ai essayé (échoué) d'installer l' pagc_normalize_addressextension mais il n'est pas clair que cela apportera le type d'amélioration que je recherche. Modifié pour ajouter des informations de surveillance selon la suggestion

Performance

Un processeur est indexé: [modifier, un seul processeur par requête, j'ai donc 5 processeurs inutilisés]

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

Exemple d'activité de disque sur la partition de données pendant qu'un proc est indexé à 100%: [modifier: un seul processeur utilisé par cette requête]

# 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

Analysez ce SQL

C'est à partir EXPLAIN ANALYZEde cette requête:

"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"

Voir une meilleure répartition sur http://explain.depesz.com/s/vogS

aaryno
la source
1
Que fait la machine lorsque vous exécutez les requêtes? Est-ce qu'il bloque les E / S ou le goulot d'étranglement est-il ailleurs?
til_b
1
Combien d'États avez-vous chargés. J'obtiens généralement de 30 ms à 150 ms par adresse sur une boîte Windows 64 bits avec 4 à 8 Go de RAM. Habituellement, je ne travaille qu'avec 1 ou 2 états. N'ont pas fait de référence sur l'impact de plus d'états sur les performances.
LR1234567
@ LR1234567 50 états
aaryno
1
@til_b CPU est arrimé à 99,7%
aaryno
Il semble que nous allons simplement attendre les quelques semaines nécessaires pour terminer cette opération, car il s'agit d'une opération unique et qu'il nous restera beaucoup de jus une fois terminé pour suivre la charge d'exécution de 100 adresses / jour. nous vivons. Je garderai cela ouvert jusqu'à ce que nous ayons terminé au cas où quelque chose de vraiment convaincant se présenterait qui nous permettrait de contourner nos CPU chevillés.
aaryno

Réponses:

7

J'ai passé beaucoup de temps à expérimenter cela, je pense qu'il vaut mieux publier séparément car ils sont sous un angle différent.

C'est vraiment un sujet complexe, voir plus de détails dans mon article de blog sur la configuration du serveur de géocodage et le script que j'ai utilisé ., Voici juste quelques brefs résumés:

Un serveur avec seulement 2 données d'états est toujours plus rapide qu'un serveur chargé avec les 50 données d'états.

J'ai vérifié cela avec mon ordinateur personnel à différents moments et avec deux serveurs Amazon AWS différents.

Mon serveur de niveau gratuit AWS avec 2 états de données n'a que 1 Go de RAM, mais il a des performances cohérentes de 43 à 59 ms pour les données avec 1 000 enregistrements et 45 000 enregistrements.

J'ai utilisé exactement la même procédure de configuration pour un serveur AWS 8G RAM avec tous les états chargés, exactement le même script et les mêmes données, et les performances ont chuté à 80 ~ 105 ms.

Ma théorie est que lorsque le géocodeur ne peut pas correspondre exactement à l'adresse, il a commencé à élargir la plage de recherche et à ignorer une partie, comme le code postal ou la ville. C'est pourquoi le document de géocodage se vante qu'il peut recoloniser l'adresse avec un mauvais code postal, bien qu'il ait fallu 3000 ms.

Avec seulement 2 états chargés, le serveur prendra beaucoup moins de temps dans une recherche infructueuse ou une correspondance avec un score très faible, car il ne peut rechercher que dans 2 états.

J'ai essayé de limiter cela en définissant le restrict_regionparamètre sur les multipolygones d'état dans la fonction de géocodage, en espérant que cela éviterait la recherche infructueuse car je suis presque sûr que la plupart des adresses ont un état correct. Comparez ces deux versions:

  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 seule différence apportée par la deuxième version est que, normalement, si j'exécute à nouveau la même requête immédiatement, cela sera beaucoup plus rapide car les données associées ont été mises en cache, mais la deuxième version a désactivé cet effet.

Donc, cela restrict_regionne fonctionne pas comme je le souhaitais, peut-être a-t-il simplement été utilisé pour filtrer le résultat de plusieurs hits, pas pour limiter les plages de recherche.

Vous pouvez régler un peu votre conf postgregre.

Le suspect habituel d'installer des index manquants, l'analyse sous vide n'a pas fait de différence pour moi, car le script de téléchargement a déjà effectué la maintenance nécessaire, sauf si vous les avez gâchés.

Cependant, la configuration de postgre conf selon ce post a aidé. Mon serveur à grande échelle avec 50 états avait 320 ms avec une configuration par défaut pour certaines données de forme pire, il s'est amélioré à 185 ms avec 2G shared_buffer, 5G cache, et est allé à 100 ms plus loin avec la plupart des paramètres ajustés selon ce post.

Ceci est plus pertinent pour les postgis et leurs paramètres semblaient similaires.

La taille du lot de chaque commit importait peu pour mon cas. La documentation du géocodage a utilisé une taille de lot 3. J'ai expérimenté des valeurs de 1, 3, 5 à 10. Je n'ai trouvé aucune différence significative avec cela. Avec une taille de lot plus petite, vous effectuez plus de validations et de mises à jour, mais je pense que le vrai col de la bouteille n'est pas là. En fait, j'utilise maintenant la taille de lot 1. Parce qu'il y a toujours une adresse mal formée inattendue qui provoquera une exception, je définirai le lot entier avec erreur comme ignoré et continuerai pour les lignes restantes. Avec la taille de lot 1, je n'ai pas besoin de traiter la table une deuxième fois pour géocoder les bons enregistrements possibles du lot marqués comme ignorés.

Bien sûr, cela dépend de la façon dont fonctionne votre script de commandes. Je posterai mon script avec plus de détails plus tard.

Vous pouvez essayer d'utiliser normaliser l'adresse pour filtrer les mauvaises adresses si cela convient à votre utilisation. J'ai vu quelqu'un mentionner cela quelque part, mais je ne savais pas comment cela fonctionne puisque la fonction de normalisation ne fonctionne qu'au format, elle ne peut pas vraiment vous dire quelle adresse n'est pas valide.

Plus tard, j'ai réalisé que si l'adresse est manifestement en mauvais état et que vous souhaitez les ignorer, cela pourrait aider. Par exemple, il manque beaucoup d'adresses au nom de la rue ou même aux noms de rue. Normaliser toutes les adresses en premier sera relativement rapide, puis vous pouvez filtrer la mauvaise adresse évidente pour vous, puis les ignorer. Cependant, cela ne convenait pas à mon utilisation, car une adresse sans numéro de rue ou même nom de rue pouvait toujours être mappée sur la rue ou la ville, et ces informations sont toujours utiles pour moi.

Et la plupart des adresses qui ne peuvent pas être géocodées dans mon cas ont en fait tous les champs, mais il n'y a aucune correspondance dans la base de données. Vous ne pouvez pas filtrer ces adresses simplement en les normalisant.

EDIT Pour plus de détails, consultez mon article de blog sur la configuration du serveur de géocodage et le script que j'ai utilisé .

EDIT 2 J'ai fini de géocoder 2 millions d'adresses et j'ai fait beaucoup de nettoyage sur les adresses en fonction du résultat du géocodage. Avec une meilleure entrée nettoyée, le prochain travail par lots s'exécute beaucoup plus rapidement. Par nettoyer, je veux dire que certaines adresses sont manifestement erronées et doivent être supprimées, ou avoir un contenu inattendu pour que le géocodeur cause un problème de géocodage. Ma théorie est la suivante: la suppression des mauvaises adresses peut éviter de gâcher le cache, ce qui améliore considérablement les performances des bonnes adresses.

J'ai séparé l'entrée en fonction de l'état pour m'assurer que chaque travail peut avoir toutes les données nécessaires pour le géocodage mises en cache dans la RAM. Cependant, chaque mauvaise adresse dans le travail oblige le géocodeur à rechercher dans plus d'états, ce qui pourrait gâcher le cache.

dracodoc
la source
Excellente réponse. Sur ma boîte, il se trouve que le filtrage de l'état accélère la correspondance d'environ 50 (!) Mais je pense que je peux avoir des problèmes d'index.
ako
2
  1. Selon ce fil de discussion , vous êtes censé utiliser la même procédure de normalisation pour traiter les données Tiger et votre adresse d'entrée. Étant donné que les données Tiger ont été traitées avec le normalisateur intégré, il est préférable d'utiliser uniquement le normalisateur intégré. Même si vous avez fait fonctionner pagc_normalizer, cela peut ne pas vous aider si vous ne l'utilisez pas pour mettre à jour les données Tiger.

    Cela étant dit, je pense que geocode () appellera de toute façon le normalisateur, donc normaliser l'adresse avant que le géocodage ne soit pas vraiment utile. Une utilisation possible du normalisateur pourrait être de comparer l'adresse normalisée et l'adresse renvoyée par geocode (). Avec les deux normalisés, il pourrait être plus facile de trouver le mauvais résultat de géocodage.

    Si vous pouvez filtrer la mauvaise adresse hors du géocodage par le normalisateur, cela sera vraiment utile. Cependant, je ne vois pas que le normalisateur ait quelque chose comme un score ou une note de match.

  2. Le même fil de discussion a également mentionné un commutateur de débogage geocode_addresspour afficher plus d'informations. Le nœud a geocode_addressbesoin d'une entrée d'adresse normalisée.

  3. Le géocodeur est rapide pour une correspondance exacte mais prend beaucoup plus de temps pour les cas difficiles. J'ai trouvé qu'il y a un paramètre restrict_regionet j'ai pensé que cela limiterait peut-être la recherche infructueuse si je définissais la limite en tant qu'état, car je suis pratiquement sûr de l'état dans lequel il se trouverait. l'adresse correcte, même si cela prend un certain temps.

    Alors peut-être que le géocodeur cherchera dans tous les endroits possibles si la première recherche exacte ne correspond pas. Cela permet de traiter les entrées avec quelques erreurs, mais aussi de ralentir la recherche.

    Je pense que c'est bon pour un service interactif d'accepter des entrées avec des erreurs, mais parfois nous pouvons vouloir abandonner un petit ensemble de mauvaises adresses pour avoir de meilleures performances dans le géocodage par lots.

dracodoc
la source
Quel a été l'impact restrict_regionsur le timing lorsque vous avez défini le bon état? De plus, à partir du fil de discussion postgis-users auquel vous avez lié ci-dessus, ils mentionnent spécifiquement avoir des problèmes avec des adresses comme celles 1020 Highway 20que j'ai rencontrées également.
aaryno
La définition de l'état correct ne s'améliorera probablement pas, car si l'adresse est bien formatée, le géocodeur peut de toute façon obtenir l'état correct.
dracodoc
1

Je vais publier cette réponse mais j'espère qu'un autre contributeur aidera à décomposer les éléments suivants, ce qui, je pense, brossera un tableau plus cohérent:

Quel est l'impact du nombre d'états chargés sur le géocodage? J'ai tous les 50 et je vois une performance beaucoup plus faible que @ LR1234567 (c'est-à-dire 8 fois pargeocode ).

Quelle est la méthode la plus efficace pour le géocodage en masse? J'exécute un processus en série, exécutant des lots de 100 à plusieurs reprises jusqu'à ce que le backload complet soit terminé. Une approche multithread serait préférable, mais quelles approches sont recommandées?

Quel est l'impact de la virtualisation sur le géocodage PostgreSQL? Je suppose que 10% sur la base de certains autres messages, mais je n'ai pas confiance en cette réponse

Maintenant ma réponse, qui n'est qu'une anecdote:

Le meilleur que j'obtiens (basé sur une seule connexion) est une moyenne de 208 ms par geocode. Cela est mesuré en sélectionnant des adresses au hasard dans mon ensemble de données, qui s'étend aux États-Unis. Il comprend des données sales mais les geocodes les plus anciens ne semblent pas être mauvais de manière évidente.

L'essentiel est que je semble être lié au processeur et qu'une seule requête est liée à un seul processeur. Je peux paralléliser cela en faisant fonctionner plusieurs connexions avec UPDATEse produisant sur des segments complémentaires de la addresses_to_geocodetable en théorie. En attendant, je reçois geocodeen moyenne 208 ms sur l'ensemble de données national. La distribution est biaisée à la fois en termes de localisation de la plupart de mes adresses et de durée (par exemple, voir l'histogramme ci-dessus) et dans le tableau ci-dessous.

Jusqu'à présent, ma meilleure approche consiste à le faire par lots de 10000, avec une amélioration estimable par rapport à la production de plus par lot. Pour des lots de 100, j'obtenais environ 251 ms, avec 10000 j'obtiens 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";

Je dois citer des noms de champs à cause de la façon dont RPostgreSQL crée les tables avec dbWriteTable

C'est à peu près 4 fois plus vite que si je les enregistre un disque à la fois. Quand je les fais un à la fois, je peux obtenir une ventilation par état (voir ci-dessous). J'ai fait cela pour vérifier et voir si un ou plusieurs des états TIGER avaient une mauvaise charge ou un mauvais indice, ce qui, je m'attendais à entraîner de mauvaises geocodeperformances au niveau de l'état. J'ai évidemment de mauvaises données (certaines adresses sont même des adresses e-mail!), Mais la plupart sont bien formatées. Comme je l'ai déjà dit, certaines des requêtes les plus anciennes n'ont pas de lacunes évidentes dans leur format. Vous trouverez ci-dessous un tableau du nombre, du temps de requête minimum, du temps de requête moyen et du temps de requête maximum pour les états de 3000 adresses aléatoires de mon ensemble de données:

       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
la source