Вывод соответствующих объявлений типа базы данных из строк в Python - PullRequest
1 голос
/ 04 июня 2009

Я строю некоторые таблицы Postgres из словарей Python, где пары {'key': 'value'} соответствуют столбцу 'key' и полю 'value'. Они генерируются из файлов .dbf - теперь я передаю содержимое файлов .dbf в сценарий, который возвращает список слов, таких как:

{'Warngentyp': '', 'Lon': '-81.67170', 'Zwatch_war': '0', 'State':...

В настоящее время я помещаю их в базу данных sqlite без объявлений типов, затем выгружаю их в файл .sql, редактирую схему вручную и импортирую в Postgres.

Я бы хотел иметь возможность выводить правильные объявления типов, в основном перебирать список строк, таких как ['0', '3', '5'] или ['ga', 'ca', 'tn' ] или ['-81.009', '135.444', '-80.000'] и сгенерируйте что-то вроде 'int', 'varchar (2)', 'float'. (Я был бы одинаково счастлив с инструментами Python, Postgres или SQLite.)

Существует ли пакет, который это делает, или простой способ его реализации?

Ответы [ 4 ]

5 голосов
/ 04 июня 2009

Не используйте eval. Если кто-то вставит неверный код, он может использовать вашу базу данных или сервер.

Вместо этого используйте эти

def isFloat(s):
try:
    float(s)
    return True
except (ValueError, TypeError), e:
    return False


str.isdigit()

А все остальное может быть varchar

2 голосов
/ 06 июня 2009

ВАМ НЕ НУЖНО ВЫРАЖАТЬ ЗАЯВЛЕНИЯ ТИПА !!!

Вы можете получить то, что вы хотите непосредственно из файлов .dbf. У каждого столбца есть имя, код типа (C = символ, N = число, D = дата (ггггммдд), L = логический (T / F), плюс дополнительные типы, если файлы взяты из Foxpro), длина (при необходимости) ) и количество десятичных знаков (для типа N).

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

Словари? Зачем? При небольшом объеме работы это программное обеспечение может быть модифицировано для создания оператора CREATE TABLE на основе этих определений столбцов, а также оператора INSERT для каждой строки данных.

Полагаю, вы используете один из нескольких опубликованных модулей Python для чтения DBF. Любой из них должен иметь необходимые вам средства: открыть файл .dbf, получить имена столбцов, получить тип столбца и т. Д., Получить каждую строку данных. Если вы недовольны используемым модулем, поговорите со мной; У меня есть неопубликованный документ, который в части чтения DBF объединяет лучшие функции других, избегает худших функций, настолько же быстр, как вы получите с чистой реализацией Python, обрабатывает все типы данных Visual Foxpro и псевдо _NullFlags -колонна, обрабатывает заметки и т. д. и т. д.

НТН

========= Приложение: Когда я сказал, что вам не нужно выводить типы, вы не дали понять, что у вас есть набор полей типа C, которые содержат числа.

Поля FIPS: некоторые с, а некоторые без начальных нулей. Если вы собираетесь их использовать, вы столкнетесь с проблемой «012»! = «12»! = 12. Я бы предложил удалить ведущие нули и сохранить их в целочисленных столбцах, восстановить ведущие нули в отчетах или что-то еще, если вам действительно нужно. Почему существует 2 штаты Фипса и Фипа?

Население: в файле примера почти все целые числа. Четыре - как 40552.0000, а разумное число - пустое / пустое. Вы, кажется, считаете население важным и спрашиваете: «Возможно ли, что небольшой процент полей населения содержит ....?» Все возможно в данных. Не удивляйтесь и не размышляйте, исследуйте! Я настоятельно рекомендую вам отсортировать данные в порядке популяции и просмотреть их; Вы обнаружите, что несколько мест в одном штате имеют одинаковое количество населения. Например. В штате Нью-Йорк есть 35 мест, чей pop'n указан как 8 008 278; они распределены по 6 округам. 29 из них имеют значение PL_FIPS 51000; 5 имеют 5100 - выглядит как проблема с завершающим нулем: - (

Совет для выбора между float и int: try anum = float (chars) first ; если это удастся, проверьте, если int (anum) == anum.

ID: замечательный «уникальный ID»; 59 случаев, когда это не int - несколько в Канаде (на веб-сайте говорилось «города США»; это артефакт какого-то неразрешенного пограничного спора?), Некоторые содержали слово «Number», а некоторые пустые.

Низко висящий фрукт: я бы подумал, что на самом деле целое число было целым на 0,1 дюйма над землей: -)

В этом есть серьезный недостаток, если все ([int (значение) ... логика:

)
>>> all([int(value) for value in "0 1 2 3 4 5 6 7 8 9".split()])
False
>>> all([int(value) for value in "1 2 3 4 5 6 7 8 9".split()])
True
>>>

Вы, очевидно, думаете, что тестируете, что все строки можно преобразовать в int, но вы добавляете райдера "и все они ненулевые". То же самое всплывают через несколько строк.

IOW, если есть только одно нулевое значение, вы объявляете, что столбец не является целым числом. Даже после исправления, если есть только одно пустое значение, вы называете его varchar. Я предлагаю следующее: посчитать, сколько пустых (после нормализации пробела (которое должно включать NBSP)), сколько квалифицируется как целое число, сколько нецелых непустых - как число с плавающей запятой, и сколько «других». Проверьте «другие»; решить, следует ли отклонить или исправить; повторяй пока не доволен: -)

Я надеюсь, что это поможет.

1 голос
/ 06 июня 2009

Спасибо за помощь, это немного долго для обновления, вот как я объединил ответы. Я начинаю со списка таких диктовок, сгенерированных из файла dbf:

dbf_list = [{'Warngentyp': '', 'Lon': '-81.67170', 'Zwatch_war': '0', 'State':...

Затем функция, которая возвращает 1000 значений на столбец для проверки на лучшее объявление типа БД: {'column_name':['list', 'of', 'sample', 'values'], 'col2':['1','2','3','4'... вот так:

def sample_fields(dicts_, number=1000): #dicts_ would be dbf_list from above
    sample = dict([[item, []] for item in dicts_[1]])
    for dict_ in dicts_[:number]:
        for col_ in dict_:
            sample[col_].append(dict_[col_])
    return sample

Затем вы комбинируете подход Неизвестный и jacob: varchar - это хорошее значение по умолчанию, и для всего остального достаточно плавающих и целых, all ясно и быстро:

def find_typedefs(sample_dict): #arg is output of previous function
    defs_ = {}
    for key in sample_dict:
        defs_[key] = 'varchar(255)'
        try:
            if all([int(value) for value in sample_dict[key]]):
                defs_[key] = 'int'
        except:
            try:
                if all([float(value) for value in sample_dict[key]]):
                    defs_[key] = 'float'
            except:
                continue
    return defs_

Затем отформатируйте возвращенный dict в оператор create table, переберите значения в исходном большом списке и введите их в базу данных. Это прекрасно работает, теперь я пропускаю промежуточный шаг sqlite, еще раз спасибо.

Обновление для Джона Мачина: я использую библиотеку shp2pgsql, поставляемую с PostGIS. Он создает схему, подобную приведенной ниже, с источником, подобным this :

   Column   |         Type          | 
------------+-----------------------+-
 gid        | integer               |
 st_fips    | character varying(7)  | 
 sfips      | character varying(5)  | 
 county_fip | character varying(12) | 
 cfips      | character varying(6)  | 
 pl_fips    | character varying(7)  | 
 id         | character varying(7)  | 
 elevation  | character varying(11) | 
 pop_1990   | integer               | 
 population | character varying(12) | 
 name       | character varying(32) | 
 st         | character varying(12) | 
 state      | character varying(16) | 
 warngenlev | character varying(13) | 
 warngentyp | character varying(13) | 
 watch_warn | character varying(14) | 
 zwatch_war | bigint                | 
 prog_disc  | bigint                | 
 zprog_disc | bigint                | 
 comboflag  | bigint                | 
 land_water | character varying(13) | 
 recnum     | integer               | 
 lon        | numeric               | 
 lat        | numeric               | 
 the_geom   | geometry              | 

Есть вещи, которые должны быть не правы - Fips - это федеральный стандарт обработки информации, и он должен быть целым числом от 0 до 100 000. Население, высота и т. Д. Может быть, у меня есть более специфичный для postgres вопрос, я бы не возражал потерять небольшой объем данных или поместить его в таблицу на предмет ошибок или чего-то еще, пытаясь изменить тип, скажем, поля населения. , Насколько строга проверка типа dbf? Например, я вижу, что популяция на shp2pgsql - это varchar (12). Возможно ли, что некоторый небольшой процент полей населения содержит что-то вроде «2445 Est.»? Если я выберу подход, изложенный в этом вопросе, с первой тысячью записей, я получу схему, подобную этой:

   Column   |          Type          |
------------+------------------------+-
 warngentyp | character varying(255) | 
 lon        | double precision       | 
 zwatch_war | character varying(255) | 
 state      | character varying(255) | 
 recnum     | character varying(255) | 
 pop_1990   | integer                | 
 land_water | character varying(255) | 
 elevation  | integer                | 
 prog_disc  | integer                | 
 comboflag  | character varying(255) | 
 sfips      | integer                | 
 zprog_disc | integer                | 
 pl_fips    | integer                | 
 county_fip | integer                | 
 population | integer                | 
 watch_warn | integer                | 
 name       | character varying(255) | 
 st         | character varying(255) | 
 lat        | double precision       | 
 st_fips    | integer                | 
 cfips      | integer                | 
 id         | integer                | 
 warngenlev | integer                |

С другой стороны, если я проверяю каждое значение в all (['list', 'of', 'everything' ...]), я получаю схему, более похожую на первую. Я могу терпеть небольшую потерю данных здесь - если запись в каком-то городе неправильная и это не оказывает существенного влияния на численность населения и т. Д.

Я использую только старый пакет под названием dbview для передачи файлов dbf в эти сценарии - я не пытаюсь отобразить какие-либо собственные возможности формата. Я предположил, что shp2pgsql выбрал бы низко висящий фрукт в этом отношении. Любые предложения для dbview или другого пакета приветствуются - хотя есть и другие случаи, когда я могу не работать с файлами dbf и в любом случае мне нужно будет найти лучшие типы. Я также собираюсь задать вопрос о postgresql, чтобы узнать, смогу ли я найти решение на этом уровне.

1 голос
/ 04 июня 2009

Вы можете небезопасно определять целые числа и числа с плавающей запятой по type(eval(elem)), где elem - элемент списка. (Но тогда вам нужно проверить elem на возможный плохой код)

Более безопасный способ может быть следующим:

a = ['24.2', '.2', '2']
try:
    if all(elem.isdigit() for elem in a):
        print("int")
    elif all(float(elem) for elem in a):
        print("float")
except:
    i = len(a[0])
    if all(len(elem)==i for elem in a):
        print("varchar(%s)"%i)
    else:
        print "n/a"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...