2009-06-04 8 views
1

私は{'key': 'value'}の対が 'key'列と 'value'列に対応するPython辞書からいくつかのPostgres表を構築しています。これらは、.DBFファイルから生成されている - 私は今、パイプのようなdictsのリストを返すスクリプトへの.dbfファイルの内容:Pythonの文字列から適切なデータベース型宣言を推測する

{'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ツールにも満足しています)

これを実行するパッケージ、またはそれを実装する簡単な方法はありますか?

答えて

2

あなたは型宣言を引き出す必要はありません!

.dbfファイルから直接必要なものを派生させることができます。各列には名前、タイプコード(C =文字、N =番号、D =日付(yyyymmdd)、L =論理(T/F)、ファイルがFoxproのものであればさらにタイプ)、長さ)、小数点以下桁数(N型の場合)などがあります。

各データを適切なPythonデータタイプに変換するためにその情報を使用するのに必要な.dbfファイルからデータを掘り起こすために使用したソフトウェアです。

辞書?どうして?少量の作業では、そのソフトウェアを変更して、これらの列定義に基づくCREATE TABLEステートメントと各データ行のINSERTステートメントを生成することができます。

あなたは、公開されているいくつかのPython DBF読書モジュールの1つを使用していると推測します。それらのいずれかが必要な機能を持っている必要があります:.dbfファイルを開き、列名を取得し、列の種類などの情報を取得し、データの各行を取得します。使用しているモジュールに不満がある場合は、私に相談してください。私はDBFを読む限り、他のより良い機能を組み合わせ、最悪の機能を避け、純粋なPythonの実装と同じ速さで、すべてのVisual Foxproデータ型と_NullFlags擬似-column、などなど、memoesを扱う

HTH

========= 補遺:私はあなたがタイプを推測する必要はないと言ったときは 、あなたはそれが平野作っていませんでしたあなたは数字を含んだCタイプの畑の束を持っていたことになります。

FIPSフィールド:一部のフィールドは先頭にゼロがなく、一部がゼロです。あなたがそれらを使うつもりなら、あなたは '012'!= '12'!= 12の問題に直面します。私は先頭のゼロを取り除き、整数列に保持し、レポートの先頭のゼロを復元するか、あるいは本当に必要な場合は何でも復元することをお勧めします。なぜ、州の旗と郡の旗が2つずつあるのですか?

人口:サンプルファイルでは、ほぼすべてが整数です。 4つは40552.0000のようなもので、合理的な数字は空白/空です。あなたは人口を重要視しているように見えますが、「人口のいくつかの小さなフィールドには....が含まれている可能性はありますか?データには何でも可能です。不思議と推測せずに、調査してください!私はあなたのデータを人口順にソートしてそれを目にすることを強くお勧めします。同じ州の複数の場所が同じ人口数を共有していることがわかります。例えば。ニューヨーク州にはpop'nが8,008,278と記載されている35の場所があります。彼らは6つの郡に広がっています。それらのうち29個はPL_FIPS値が51000であり、 5 5100を持っている - floatとint型の間で決定する

ヒント:-(末尾のゼロ問題のようになります。anum =フロート(文字)最初を試し、それが成功した場合、INT(anum)== anumかどうかを確認。

ID:素晴らしい "ユニークID";それは、カナダでいくつかのint - いくつかの場合(ウェブサイトは "米国の都市"と呼ばれています、これはいくつかの未解決の国境紛争の人工物ですか?

低吊り果物:その人口を実際に整数倍したものは地上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に変換できることテストしていると思いますが、あなたがしていますライダーを追加する "とすべてゼロではない"。 Dittoは数行後にフロートします。

IOW値がゼロの場合は、列が整数ではないことを宣言します。 それを修正した後でも、ただ1つの空の値がある場合は、それをvarcharと呼びます。 私が提案しているのは空白(NBSPを含むべきである空白を正規化した後)の数、整数としての修飾数、非整数でない空でない空の数、floatとしての数、その他の数です。 "他の"ものをチェックしてください。拒否するか修正するかを決定する。幸せまで繰り返す:-)

私はこれが役立つことを望みます。

+0

+1、私はあなたのモジュールを試してみたいと思っています。私は実際に何かのために設計されたライブラリを使用しているかもしれない、私はあなたのコメントごとに自己回答を拡大し、あなたの投稿に追加したい場合はここにチェックします。 – unmounted

+0

後でお送りします。 –

+0

これは私が見つけることができたその種の最高のデータセットです。これまでのテストフィールドや、失敗した挿入に使用しているエラーログにはゼロがありません。私はデータを掘り下げており、基本的にはコロラド州とカナダとニュージャージー州の88行の不整列行があり、名前の算術演算子のようなものがあります。私は.2%のエラー率に達しています。 40k +のうち88がOKで、カナダはとにかく除外されるべきです。 "クレジットアイランド、アイオワ"は持っているといいですね。私の目標は、輸入クラスを自動化することでした。私はそこに着いています。私はあなたのモジュールを勉強して使用しています.BTW、たくさんのものを手に入れます... – unmounted

1

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

私は評価が安全ではないと(私の場合)、それは良い答えだったと思います。私は管理者がデータをアップロードしているかもしれないので、ある仮定ではリスクがあるかもしれませんが、私は信頼できないデータを規定しませんでした。 – unmounted

5

を行うには可能性は、evalを使用しないでください。誰かが悪いコードを挿入した場合、あなたのデータベースやサーバーを使い切ることができます。

代わりにこれらの

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


str.isdigit() 

を使用し、他のすべては助けのための

+3

"誰かが悪いコードを挿入した場合、あなたのデータベースやサーバーを汚染する可能性があります。"それはどうやって起こりますか?悪いコードを挿入する可能性のある「誰か」は誰ですか?どのような "悪いコード"は、サーバーをホースしますか? 'import sys; sys.crash_server(True) 'を列の値として使用しますか? –

+0

+1:明示的な型比較を避けます。 –

+0

私はevalの安全でないことを忘れました。私は今のところそれを更新しました:) – jacob

1

おかげvarchar型することができ、これは、更新のために少し長いです、ここで私は答えを組み合わせた方法です。

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 
私はDBFファイルから生成され、このようなdictsのリストを始めていますその後

あなたが不明とヤコブのアプローチを組み合わせる:varchar型が良いデフォルトで、山車とint型は、他のすべてのために基本的には十分にあり、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のステップをスキップしている、もう一度ありがとう。

John Machinのアップデート:私はPostGISと一緒に配布されているshp2pgsqlライブラリを使用しています。それはthis oneのようなソースと、以下のようなスキーマを作成します。

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)です。いくつかの小さな人口フィールドに「2,445 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    | 

、:私は最初の1000件のレコードを、この質問に記載されたアプローチを取る場合、私はこのようなスキーマを取得します'of'、 'everything' ...])、最初のスキーマに近いスキーマが得られます。

私はdbviewと呼ばれる古いパッケージを使用してdbfファイルをパイプ処理していますが、私はちょっとしたデータ損失を許容できます。これらのスクリプトには、私はフォーマットのネイティブ機能をマップしようとしていません。私はshp2pgsqlがその点で低懸念の果物を選んだと仮定しました。とにかく私はdbfファイルで作業していない可能性があり、とにかく最良のタイプを見つける必要がある他のケースがありますが、dbviewまたは他のパッケージのいずれかの提案は歓迎です。私はpostgresqlに関する質問をして、そのレベルで解決策が見つかるかどうかを調べるつもりです。