Как быстро следует ожидать, что PostGIS геокодирует хорошо отформатированные адреса?

17

Как быстро следует ожидать, что PostGIS геокодирует хорошо отформатированные адреса?

Я установил PostgreSQL 9.3.7 и PostGIS 2.1.7, загрузил данные о стране и все данные о состоянии, но обнаружил, что геокодирование намного медленнее, чем я ожидал. Я поставил свои ожидания слишком высоко? Я получаю в среднем 3 отдельных геокода в секунду. Мне нужно сделать около 5 миллионов, и я не хочу ждать три недели для этого.

Это виртуальная машина для обработки гигантских матриц R, и я установил эту базу данных сбоку, чтобы конфигурация выглядела немного глупо. Если серьезное изменение в конфигурации виртуальной машины поможет, я могу изменить конфигурацию.

Аппаратные характеристики

Память: 65 ГБ Процессоры: 6 lscpuдает мне это:

# 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

ОС является Centos, uname -rvдает это:

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

Конфиг 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"

Основываясь на предыдущих предложениях к этим типам запросов, я увеличил shared_buffersв postgresql.confфайле до 1/4 доступной оперативной памяти и эффективный размер кэша до 1/2 оперативной памяти:

shared_buffers = 16096MB     
effective_cache_size = 31765MB

У меня installed_missing_indexes()и (после устранения дублирующих вставок в некоторые таблицы) ошибок не было.

Пример геокодирования SQL # 1 (пакетный) ~ среднее время составляет 2,8 / сек

Я следую примеру из http://postgis.net/docs/Geocode.html , который заставляет меня создать таблицу, содержащую адрес геокодирования, а затем выполнить 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";

Я использую пакет размером 1000 и выше, и он возвращается через 337,70 секунд. Это немного медленнее для небольших партий.

Пример геокодирования SQL # 2 (строка за строкой) ~ среднее время составляет 1,2 / сек

Когда я копаюсь в своих адресах, выполняя геокодирование по одному за один раз с оператором, который выглядит следующим образом (кстати, пример ниже занял 4,14 секунды),

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;

это немного медленнее (2,5 раза на запись), но я могу посмотреть на распределение времени запросов и увидеть, что это меньшинство длинных запросов, которые замедляют это больше всего (только первые 2600 из 5 миллионов имеют время поиска). То есть верхние 10% занимают в среднем около 100 мс, нижние 10% - в среднем 3,69 секунды, а среднее значение составляет 754 мс, а медиана - 340 мс.

# 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]

Время геокодирования для первых 2600 строк

Другие мысли

Если я не смогу добиться увеличения производительности на порядок, я подумал, что мог бы, по крайней мере, сделать обоснованное предположение о прогнозировании медленного времени геокодирования, но мне не очевидно, почему медленные адреса, кажется, занимают намного больше времени. Я запускаю исходный адрес через шаг пользовательской нормализации, чтобы убедиться, что он правильно отформатирован, прежде чем geocode()функция получит его:

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

где myAddress- [Address], [City], [ST] [Zip]строка, скомпилированная из таблицы адресов пользователей из базы данных, отличной от postgresql.

Я попытался (не удалось) установить pagc_normalize_addressрасширение, но не ясно, принесет ли это то улучшение, которое я ищу. Отредактировано, чтобы добавить информацию мониторинга согласно предложению

Производительность

Один ЦП привязан: [редактировать, только один процессор на запрос, поэтому у меня 5 неиспользуемых ЦП]

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

Пример активности диска на разделе данных, в то время как один процесс привязан к 100%: [edit: только один процессор используется этим запросом]

# 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

Проанализируйте этот SQL

Это из EXPLAIN ANALYZEэтого запроса:

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

Посмотреть более подробную разбивку можно по адресу http://explain.depesz.com/s/vogS

aaryno
источник
1
Что делает машина, когда вы запускаете запросы? Это блокирует IO или узкое место где-то еще?
til_b
1
Сколько штатов вы загрузили. Я обычно получаю от 30 мс до 150 мс на адрес в 64-битной коробке Windows с 4-8 ГБ оперативной памяти. Обычно, хотя я работаю только с 1 или 2 государствами. Не было сделано ни одного эталона по влиянию большего количества штатов на производительность.
LR1234567
@ LR1234567 50 штатов
aaryno
1
@til_b ЦП привязан на 99,7%
aaryno
Похоже, мы просто подождем пару недель, которые потребуются, чтобы закончить эту вещь, так как это разовая вещь, и у нас останется много сока, как только это будет сделано, чтобы не отставать от нагрузки на 100 адресов / день выполнения мы переживаем. Я буду держать это открытым до тех пор, пока мы не закончим на случай, если появится что-то действительно убедительное, что позволит нам обойти наши привязанные процессоры.
aaryno

Ответы:

7

Я провел много времени, экспериментируя с этим, я думаю, что лучше публиковать отдельно, так как они под другим углом.

Это действительно сложная тема, подробности см. В моем блоге о настройке сервера геокодирования и сценарии, который я использовал . Вот лишь несколько кратких резюме:

Сервер с данными только 2 состояний всегда быстрее, чем сервер, загруженный всеми данными 50 состояний.

Я проверял это на своем домашнем компьютере в разное время и на двух разных серверах Amazon AWS.

Мой бесплатный сервер уровня AWS с данными о 2 состояниях имеет только 1 ГБ ОЗУ, но обеспечивает стабильную производительность от 43 до 59 мс для данных с 1000 и 45 000 записей.

Я использовал точно такую ​​же процедуру установки для сервера AWS 8G RAM со всеми загруженными состояниями, точно такими же сценарием и данными, и производительность упала до 80 ~ 105 мс.

Моя теория заключается в том, что, когда геокодер не может точно соответствовать адресу, он начинает расширять диапазон поиска и игнорирует некоторую часть, например почтовый индекс или город. Вот почему документ геокодирования может похвастаться тем, что он может повторно определить адрес с неверным почтовым индексом, хотя это заняло 3000 мс.

Если загружены только данные о 2 состояниях, серверу потребуется гораздо меньше времени для бесполезного поиска или поиска с очень низкой оценкой, поскольку он может выполнять поиск только в 2 состояниях.

Я попытался ограничить это, установив restrict_regionпараметр для мультиполигонов состояний в функции геокодирования, надеясь, что это позволит избежать бесполезного поиска, так как я почти уверен, что большинство адресов имеют правильное состояние. Сравните эти две версии:

  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';

Единственное отличие второй версии заключается в том, что обычно, если я сразу же запускаю тот же запрос, он будет выполняться намного быстрее, потому что связанные данные были кэшированы, но вторая версия отключила этот эффект.

Так что restrict_regionне работает, как я хотел, может быть, он просто использовался для фильтрации результатов многократного попадания, а не для ограничения диапазонов поиска.

Вы можете немного настроить свой postgre conf.

Обычно подозреваемый в установке отсутствующих индексов, анализ вакуума не имел для меня никакого значения, потому что скрипт загрузки уже выполнил необходимое обслуживание, если только вы не испортили его.

Однако установка postgre conf в соответствии с этим постом помогла. Мой полноразмерный сервер с 50 состояниями имел 320 мс с конфигурацией по умолчанию для некоторых данных худшей формы, он улучшился до 185 мс с 2G shared_buffer, 5G кешем и пошел до 100 мс далее, с большинством настроек, настроенных в соответствии с этим постом.

Это больше относится к postgis, и их настройки кажутся похожими.

Размер партии каждого коммита не имел большого значения для моего случая. В документации по геокодированию использовалась партия размером 3. Я экспериментировал со значениями от 1, 3, 5 до 10. Я не нашел существенной разницы с этим. С меньшим размером партии вы делаете больше коммитов и обновлений, но я думаю, что настоящая бутылочная горловина не здесь. На самом деле я использую размер партии 1 сейчас. Поскольку всегда есть какой-то неожиданный неверно сформированный адрес, это вызовет исключение, я установлю весь пакет с ошибкой как проигнорированный и продолжу для оставшихся строк. При размере пакета 1 мне не нужно обрабатывать таблицу во второй раз, чтобы геокодировать возможные хорошие записи в пакете, помеченные как проигнорированные.

Конечно, это зависит от того, как работает ваш пакетный скрипт. Я опубликую свой сценарий с более подробной информацией позже.

Вы можете попытаться использовать нормализованный адрес, чтобы отфильтровать неверный адрес, если он подходит вам. Я видел, как кто-то где-то упоминал об этом, но я не был уверен, как это работает, поскольку функция нормализации работает только в формате, он не может точно сказать, какой адрес является недействительным.

Позже я понял, что если адрес явно не в форме и вы хотите их пропустить, это может помочь. Например, у меня много адресов, в которых отсутствуют названия улиц или даже названия улиц. Сначала нормализовать все адреса будет относительно быстро, затем вы можете отфильтровать очевидный неверный адрес, а затем пропустить их. Однако это не подходило для моего использования, поскольку адрес без номера улицы или даже названия улицы все еще можно сопоставить с улицей или городом, и эта информация все еще полезна для меня.

И большинство адресов, которые не могут быть геокодированы в моем случае, на самом деле имеют все поля, просто нет совпадений в базе данных. Вы не можете фильтровать эти адреса, просто нормализуя их.

РЕДАКТИРОВАТЬ Для получения более подробной информации см. Мой пост в блоге о настройке сервера геокодирования и сценарии, который я использовал .

РЕДАКТИРОВАТЬ 2 Я закончил геокодирование 2 миллиона адресов и сделал много очистки на основе результатов геокодирования. Благодаря более чистому вводу следующее пакетное задание выполняется намного быстрее. Под чистым я подразумеваю, что некоторые адреса явно неверны и должны быть удалены, или имеют геокодер с неожиданным содержимым, чтобы вызвать проблемы с геокодированием. Моя теория такова: удаление плохих адресов может помочь избежать путаницы в кеше, что значительно повышает производительность хороших адресов.

Я разделил входные данные на основе состояния, чтобы убедиться, что в каждом задании все данные, необходимые для геокодирования, кэшируются в оперативной памяти. Однако каждый неверный адрес в задании заставляет геокодер искать в большем количестве состояний, что может испортить кеш.

dracodoc
источник
Отличный ответ. На моем боксе, как это бывает, фильтрация по состоянию ускоряет совпадение примерно в 50 (!) Раз, но я подозреваю, что у меня могут быть проблемы с индексами.
АКО
2
  1. Согласно этой ветке обсуждения , вы должны использовать одну и ту же процедуру нормализации для обработки данных Tiger и вашего входного адреса. Поскольку данные Tiger были обработаны с помощью встроенного нормализатора, лучше использовать только встроенный нормализатор. Даже если вы запустили pagc_normalizer, он может вам не помочь, если вы не используете его для обновления данных Tiger.

    При этом я думаю, что geocode () все равно вызовет нормализатор, поэтому нормализация адреса до геокодирования может быть не очень полезна. Одним из возможных применений нормализатора может быть сравнение нормализованного адреса и адреса, возвращаемого функцией geocode (). С их нормализацией было бы легче найти неправильный результат геокодирования.

    Если вы можете отфильтровать неверный адрес из геокода с помощью нормализатора, это действительно поможет. Однако я не вижу, чтобы у нормализатора было что-то вроде оценки матча или рейтинга.

  2. В той же ветке обсуждения также упоминался переключатель отладки, geocode_addressчтобы показать больше информации. Узлу geocode_addressнужен нормализованный адрес ввода.

  3. Геокодер быстр для точного соответствия, но в трудных случаях отнимает гораздо больше времени. Я обнаружил, что есть параметр, restrict_regionи подумал, что, возможно, он будет ограничивать бесполезный поиск, если я установлю ограничение как состояние, поскольку я почти уверен, в каком состоянии он будет находиться. Оказалось, что установка его в неправильное состояние не помешала геокоду получить правильный адрес, хотя это займет некоторое время.

    Так что, возможно, геокодер будет искать во всех возможных местах, если первый точный поиск не соответствует. Это позволяет обрабатывать ввод с некоторыми ошибками, но также делает поиск очень медленным.

    Я думаю, что интерактивному сервису полезно принимать ввод с ошибками, но иногда мы можем просто отказаться от небольшого набора неправильных адресов, чтобы повысить производительность при пакетном геокодировании.

dracodoc
источник
Каково было влияние restrict_regionна время, когда вы устанавливаете правильное состояние? Кроме того, из ветки постгис-пользователей, на которую вы ссылались выше, они упоминают, в частности, проблемы с адресами, с 1020 Highway 20которыми я столкнулся.
aaryno
Установка правильного состояния, вероятно, не улучшится, поскольку, если адрес правильно отформатирован, геокодер в любом случае может получить правильное состояние.
dracodoc
1

Я собираюсь опубликовать этот ответ, но, надеюсь, другой участник поможет разобраться в следующем, которое, я думаю, нарисует более целостную картину:

Какое влияние оказывает количество загруженных состояний на геокодирование? У меня есть все 50, и я вижу гораздо более низкую производительность, чем @ LR1234567 (то есть, 8 раз за geocode).

Какой самый эффективный метод для массового геокодирования? Я запускаю последовательный процесс, запуская партии по 100 раз, пока вся обратная загрузка не будет завершена. Многопоточный подход был бы предпочтительнее, но какие подходы рекомендуются?

Какое влияние оказывает виртуализация на геокодирование PostgreSQL? Я предполагаю, что 10% основаны на некоторых других сообщениях, но мало уверенности в этом ответе

Теперь мой ответ, который является просто анекдотом:

Лучшее, что я получаю (на основе одного соединения), составляет в среднем 208 мс за geocode. Это измеряется путем случайного выбора адресов из моего набора данных, который распространяется по всей территории США. Он включает в себя некоторые грязные данные, но самые длительные операции geocodeне кажутся плохими очевидными способами.

Суть в том, что я, кажется, привязан к процессору, и что один запрос привязан к одному процессору. Я могу распараллелить это, запустив несколько соединений с UPDATEдополняющими сегментами addresses_to_geocodeтаблицы в теории. Тем временем я получаю geocodeв среднем 208 мс для общенационального набора данных. Распределение искажено как с точки зрения того, где находится большинство моих адресов, так и с точки зрения того, сколько времени они занимают (например, см. Гистограмму выше) и таблицы ниже.

Мой лучший подход до сих пор - делать это партиями по 10000 с некоторым ощутимым улучшением по сравнению с увеличением количества партий. Для партий из 100 я получал около 251 мс, из 10000 я получал 208 мс.

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

Я должен цитировать имена полей из-за того, как RPostgreSQL создает таблицы с dbWriteTable

Это примерно в 4 раза быстрее, чем если бы я делал их по одной записи за раз. Когда я делаю их по одному, я могу получить разбивку по штатам (см. Ниже). Я сделал это, чтобы проверить и убедиться, что одно или несколько состояний TIGER имеют плохую нагрузку или индекс, что, как я ожидал, приведет к geocodeснижению производительности в зависимости от состояния. Я, очевидно, получил некоторые неверные данные (некоторые адреса даже адреса электронной почты!), Но большинство из них хорошо отформатированы. Как я уже говорил, некоторые из самых длительных запросов не имеют явных недостатков в своем формате. Ниже приведена таблица с числом, минимальным временем запроса, средним временем запроса и максимальным временем запроса для состояний из 3000 (несколько случайных адресов из моего набора данных):

       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
источник