我正在从Python词典构建一些Postgres表,其中{'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工具同样满意。)
是否存在执行此操作的程序包或实现它的简单方法?
答案 0 :(得分:5)
不要使用eval。如果有人插入了错误的代码,它可能会阻塞您的数据库或服务器。
改为使用这些
def isFloat(s):
try:
float(s)
return True
except (ValueError, TypeError), e:
return False
str.isdigit()
其他一切都可以是varchar
答案 1 :(得分:2)
你不需要宣传类型宣言!!!
您可以直接从.dbf文件派生出您想要的内容。每列都有一个名称,一个类型代码(C =字符,N =数字,D =日期(yyyymmdd),L =逻辑(T / F),如果文件来自Foxpro,还有更多类型),长度(相关时间) ),以及一些小数位(对于N型)。
用于从.dbf文件中挖掘数据所需的任何软件,使用该信息将每个数据转换为适当的Python数据类型。
字典?为什么?通过少量工作,可以修改该软件以基于这些列定义生成CREATE TABLE语句,并为每行数据生成INSERT语句。
我认为您正在使用几个已发布的Python DBF读取模块之一。其中任何一个都应该具备您需要的功能:打开.dbf文件,获取列名,获取列类型等信息,获取每行数据。如果您对所使用的模块不满意,请与我联系;我有一个未发表的,就读DBF而言,它结合了其他的更好的功能,避免了最糟糕的功能,就像你使用纯Python实现一样快,处理所有的Visual Foxpro数据类型和_NullFlags伪 - 列,处理备忘录等。
HTH
====== 附录: 当我说你不需要推断类型时,你没有说明你有一堆包含数字的C类字段。
FIPS字段:有些是有一些,有些没有前导零。如果你要使用它们,你将面临'012'!='12'!= 12问题。我建议剥离前导零并将它们保留在整数列中,在报告中恢复前导零或者如果你真的需要的话还原。为什么每个国家的fips和县fips都有2个?
填充:在示例文件中,几乎所有都是整数。四个像40552.0000,合理的数字是空白/空。你似乎认为人口是重要的,并问“有可能一小部分人口领域包含......?”数据中有任何可能的东西。不要怀疑和猜测,调查!我强烈建议您按人口顺序对数据进行排序并注意它;你会发现同一个州的多个地方共享相同的人口数。例如。在纽约州有35个地方,其pop'n被称为8,008,278;它们分布在6个县。其中29个PL_FIPS值为51000; 5有5100 - 看起来像一个尾随的零问题: - (
提示在float和int之间做出决定:首先尝试anum = float(chars) ;如果成功,检查int(anum)== anum。
ID:精彩的“唯一ID”; 59个案例不是int - 几个在加拿大(该网站称“美国城市”;这是一个未解决的边界争议的文物吗?),有些包含“数字”这个词,有些是空的。
低调的果实:我认为推断人口实际上是整数是0.1英寸以上: - )
如果全部([int(value)...逻辑:
)存在严重缺陷>>> 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后来漂了几行。
如果只有一个零值,则声明该列不是整数。 即使在修复之后,如果只有一个空值,则将其称为varchar。 我建议的是:计算有多少是空的(在规范化空格(应该包括NBSP)之后),有多少合格为整数,有多少非整数非空的合格为float,以及有多少“其他”。检查“其他”的;决定是否拒绝或修复;重复直到开心: - )
我希望其中一些有帮助。
答案 2 :(得分: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"
答案 3 :(得分:1)
感谢您的帮助,这对于更新来说有点长,这是我如何结合答案。我从一个这样的dicts列表开始,从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
然后你结合了Unknown和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步骤,再次感谢。
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 |
另一方面,如果我检查all中的每个值(['list','of','everything'...]),我会得到一个更像第一个的模式。我可以在这里容忍一些数据丢失 - 如果一些城镇的入口是错误的,并且它不会显着影响人口数据等。
我只使用一个名为dbview
的旧包来将dbf文件传输到这些脚本中 - 我不是试图映射任何格式的本机功能。我认为shp2pgsql会在这方面选择低调的果实。对dbview或其他软件包的任何建议都是受欢迎的 - 尽管在其他情况下我可能不会使用dbf文件,并且无论如何都需要找到最好的类型。我还要问一个关于postgresql的问题,看看我是否能找到该级别的解决方案。