2009-06-04 4 views
1

Ich baue einige Postgres-Tabellen aus Python-Wörterbüchern, wobei die Paare {'key': 'value'} der Spalte 'key' und field 'value' entsprechen. Diese werden erzeugt von DBF-Dateien - ich jetzt Rohr der Inhalt der DBF-Dateien in ein Skript, das eine Liste von dicts gibt wie:Ich importiere geeignete Datenbanktyp-Deklarationen aus Strings in Python

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

Derzeit bin ich diese in eine SQLite-Datenbank ohne Typdeklarationen setzen Dann wird es in eine .sql-Datei geschrieben, das Schema manuell bearbeitet und nach Postgres importiert.

Ich würde gerne in der Lage sein, die korrekten Typdeklarationen abzuleiten, im Grunde iterieren über eine Liste von Strings wie ['0', '3', '5'] oder ['ga', 'ca', 'tn '] oder [' -81.009 ',' 135.444 ',' -80.000 '] und erzeuge etwas wie' int ',' varchar (2) ',' float '. (Ich wäre genauso glücklich mit einem Python-, Postgres- oder SQLite-Tool.)

Gibt es ein Paket, das dies tut, oder einen einfachen Weg, es zu implementieren?

Antwort

2

SIE MÜSSEN DIE TYPENKENNZEICHNUNGEN NICHT INFERENZEN !!!

Sie können direkt aus den DBF-Dateien ableiten, was Sie wollen. Jede Spalte hat einen Namen, einen Typcode (C = Zeichen, N = Zahl, D = Datum (JJJJMMTT), L = Logisch (T/F), plus weitere Typen, wenn die Dateien von Foxpro stammen), eine Länge (falls relevant)), und eine Anzahl von Dezimalstellen (für Typ N).

Welche Software Sie verwendet haben, um die Daten aus den .dbf-Dateien zu ermitteln, die für die Verwendung dieser Informationen erforderlich sind, um alle Daten in den entsprechenden Python-Datentyp zu konvertieren.

Wörterbücher? Warum? Mit einer geringen Menge an Arbeit könnte diese Software modifiziert werden, um eine CREATE TABLE-Anweisung basierend auf diesen Spaltendefinitionen plus eine INSERT-Anweisung für jede Datenzeile zu erzeugen.

Ich nehme an, dass Sie eines der mehreren veröffentlichten Python DBF-Lesemodule verwenden. Jeder von ihnen sollte die Einrichtungen haben, die Sie brauchen: Öffnen Sie eine .dbf-Datei, erhalten Sie die Spaltennamen, erhalten Sie den Spaltentyp etc info, erhalten Sie jede Reihe von Daten. Wenn Sie mit dem Modul, das Sie verwenden, unzufrieden sind, sprechen Sie mit mir; Ich habe eine unveröffentlichte, die, soweit das Lesen von DBFs geht, die besseren Features der anderen kombiniert, die schlimmsten Features vermeidet, so schnell ist, wie Sie es mit einer reinen Python-Implementierung bekommen, alle Visual Foxpro-Datentypen und den _NullFlags-Pseudo behandelt -Spalte, Griffe memoes, etc etc.

HTH

========= Nachtrag: Als ich sagte, Sie nicht Typen zu schließen brauchte, hatte man nicht deutlich gemacht, dass du eine Menge Felder vom Typ C hast, die Zahlen enthielten.

FIPS-Felder: einige sind mit und einige ohne führende Nullen. Wenn Sie sie verwenden, sehen Sie sich dem Problem '012'! = '12'! = 12 gegenüber. Ich würde vorschlagen, die führenden Nullen zu entfernen und sie in ganzzahligen Spalten zu halten, führende Nullen in Berichten wiederherzustellen oder was auch immer, wenn Sie wirklich brauchen. Warum gibt es jeweils 2 Staatsfips und County Fips?

Grundgesamtheit: In der Beispieldatei sind fast alle Ganzzahlen. Vier sind wie 40552.0000, und eine angemessene Anzahl ist leer. Sie scheinen die Bevölkerung für wichtig zu halten und haben gefragt: "Ist es möglich, dass ein kleiner Prozentsatz der Bevölkerung Felder enthält ...?" In Daten ist alles möglich. Wundern Sie sich nicht und spekulieren Sie, untersuchen Sie!Ich rate Ihnen dringend, Ihre Daten in Populationsreihenfolge zu sortieren und sie zu betrachten; Sie werden feststellen, dass mehrere Orte im selben Bundesland die gleiche Bevölkerungszahl teilen. Z.B. Es gibt 35 Orte in New York, deren Pop'n mit 8.008.278 angegeben ist; Sie sind auf 6 Landkreise verteilt. 29 von ihnen haben einen PL_FIPS-Wert von 51000; 5 haben 5100 - sieht aus wie ein Hinter Null Problem :-(

Tipps für die zwischen Schwimmer entscheiden und int: versuchen anum = float (Zeichen) ersten, wenn das gelingt, überprüfen, ob int (anum) == anum

ID: wunderbare "einzigartige ID"; 59 Fälle, wo es kein int - mehrere in Kanada ist (die Website sagte "US-Städte"; ist das ein Artefakt eines ungelösten Grenzstreits?), Einige mit dem Wort ‚Nummer‘, und einige leere

niedrig hängenden Früchte. ich würde das herzuleiten gedacht, dass Bevölkerung in der Tat ganze Zahl betrug 0,1 Zoll über dem Boden war :-)

Es gibt ein schwerwiegender Fehler, dass, wenn alle ([int (value) ... Logik:

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

Sie denken offenbar, dass Sie testen, dass alle Strings umgewandelt werden können, um int, aber du bist Hinzufügen des Fahrers "und alle sind nicht Null". Dito float ein paar Zeilen später.

IOW Wenn es nur einen Nullwert gibt, deklarieren Sie, dass die Spalte keine Ganzzahl ist. Auch nach dem Fixieren, wenn es nur einen leeren Wert gibt, nennt man es varchar. Was ich vorschlage, ist: Zählen Sie, wie viele leer sind (nach der Normalisierung von Leerzeichen (die NBSP enthalten sollte)), wie viele qualifizieren als Integer, wie viele nicht-ganzzahlige Nichtleere als Float qualifizieren, und wie viele "andere". Überprüfen Sie die "anderen"; entscheiden, ob sie ablehnen oder reparieren sollen; wiederholen bis glücklich :-)

Ich hoffe, dass einige davon hilft.

+0

+1, ich würde gerne versuchen, Ihr Modul, wenn es Ihnen nichts ausmacht zu teilen Ich bin mein Benutzername bei Yahoo. Es kann sein, dass ich eine Bibliothek verwende, die wirklich für etwas anderes gedacht ist, ich habe die Selbstantwort für deine Kommentare erweitert, werde hier nachsehen, ob du deinen Beitrag hinzufügen möchtest. – unmounted

+0

Ich schicke es dir später. –

+0

Leider ist dies der beste Datensatz seiner Art, den ich gefunden habe. Bisher keine Nullen in den Testfeldern oder im Fehlerprotokoll, das ich für fehlerhafte Einfügungen verwende. Ich habe in die Daten gegraben und es gibt im Grunde 88 unverbesserliche Reihen, Orte in Colorado und Kanada und NJ mit Dingen wie arithmetischen Operatoren für Namen. Ich bin auf 0,2% Fehlerrate. 88 von 40k + ist in Ordnung, und Kanada sollte sowieso ausgeschlossen werden. "Credit Island, Iowa" wäre allerdings schön. Mein Ziel war es, eine Klasse von Importen zu automatisieren, und ich komme dorthin - ich studiere und verwende Ihr Modul, übrigens, Sie werden viele ... – unmounted

1

Sie können Integer und Floats unsicher durch type(eval(elem)) bestimmen, wobei elem ein Element der Liste ist. (Aber dann müssen Sie Elem für möglichen schlechten Code überprüfen)

Ein sicherer Weg, könnte die folgenden

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

Ich glaube auch nicht, dass die Eval war unsicher (in meinem Fall), und es war eine gute Antwort. Ich habe vielleicht Admins hochgeladen Daten so in einigen hypothetischen könnte ein Risiko sein, aber ich habe keine unzuverlässigen Daten. – unmounted

5

Verwenden Sie keine eval zu tun. Wenn jemand schlechten Code einfügt, kann er die Datenbank oder den Server straffen.

diese Statt sonst

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


str.isdigit() 

Und alles verwenden können VARCHAR

+3

"Wenn jemand schlechten Code einfügt, kann er die Datenbank oder den Server streichen." Was? Wie wird das passieren? Wer ist dieser "Jemand", der den schlechten Code einfügen könnte? Welcher "schlechte Code" schleust den Server? 'importieren sys; sys.crash_server (True) 'als Spaltenwert? –

+0

+1: vermeidet explizite Typvergleiche. –

+0

Ich habe die Unsicherheiten von Eval vergessen. Ich habe es jetzt unten aktualisiert :) – jacob

1

Danke für die Hilfe sein, das ist ein wenig für ein Update lang, hier ist, wie ich die Antworten kombiniert. Ich beginne mit einer Liste von dicts wie diese, aus einer DBF-Datei erzeugt:

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

Dann eine Funktion, die 1000 Werte pro Spalte gibt für die beste db Typdeklaration zu testen: {'column_name':['list', 'of', 'sample', 'values'], 'col2':['1','2','3','4'... wie folgt aus:

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 

Dann kombinieren Sie das Unbekannte und jacob Ansatz: varchar ist ein guter Standard und schwimmt und ints sind im Grunde genug für alles andere, ist all klar und schnell:

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_ 

Dann formatieren Sie das zurückgegebene dict in eine create table Anweisung, durchlaufen Sie die Werte in der ursprünglichen großen Liste und füttern Sie sie in die Datenbank. Es funktioniert super, ich überspringe jetzt den Zwischenschritt sqlite, nochmals vielen Dank.

Update für John Machin: Ich verwende die shp2pgsql Bibliothek verteilt mit PostGIS. Es schafft Schema wie die unten mit einer Quelle wie 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    | 

Da gibt es Sachen, die falsch sein muss - Fips die Federal Information Processing Standard ist, und es soll eine ganze Zahl zwischen 0 und so etwas wie 100.000 sein . Bevölkerung, Höhe usw.Vielleicht habe ich mehr von einer postgres spezifischen Frage, ich hätte nichts dagegen, eine kleine Menge von Daten zu verlieren, oder sie in eine Tabelle für Fehler oder etwas zu schieben, während ich versuche, den Typ auf das Bevölkerungsfeld zu ändern. Wie streng ist die Überprüfung des DBF-Typs? Zum Beispiel sehe ich, dass die Population pro shp2pgsql varchar (12) ist. Ist es möglich, dass ein kleiner Prozentsatz der Populationsfelder etwas wie "2.445 Est." Enthält? Wenn ich den Ansatz nehme ich in dieser Frage festgelegt, wobei die ersten tausend Datensätze, erhalte ich ein Schema wie folgt aus:

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    | 

Auf der anderen Seite, wenn ich jeden Wert in der alle ([ ‚Liste‘ überprüfen, "von", "alles" ...]), bekomme ich ein Schema mehr wie das erste. Ich kann hier ein wenig Datenverlust tolerieren - wenn der Eintrag für eine Stadt falsch ist und die Bevölkerungszahlen usw. nicht wesentlich beeinflusst.

Ich verwende nur ein altes Paket namens dbview, um die dbf-Dateien zu pipen in diese Skripts - ich versuche nicht, irgendwelche nativen Fähigkeiten des Formats abzubilden. Ich nahm an, dass shp2pgsql die tief hängenden Früchte in dieser Hinsicht ausgewählt hätte. Vorschläge für dbview oder ein anderes Paket sind willkommen - obwohl es auch andere Fälle gibt, in denen ich nicht mit dbf-Dateien arbeiten kann und trotzdem die besten Typen finden muss. Ich werde auch eine Frage über PostgreSQL stellen, um zu sehen, ob ich auf dieser Ebene eine Lösung finden kann.

Verwandte Themen