Jon*_*ter 5 python csv postgresql python-3.x
我正在使用Python的csv模块将数据从sql server写入csv文件,然后使用copy命令将csv文件上传到postgres数据库。问题是Python的csv编写器自动将Nulls转换为空字符串“”,并且当列为int或float数据类型时,它使我的工作失败,并且当它应为None或null值时,它将尝试插入此“”。
为了尽可能容易地与实现DB API的模块接口,将值None写为空字符串。
https://docs.python.org/3.4/library/csv.html?highlight=csv#csv.writer
保留空值的最佳方法是什么?有没有更好的方法用Python编写csv?我愿意接受所有建议。
例:
我有纬度和经度值:
42.313270000    -71.116240000
42.377010000    -71.064770000
NULL    NULL
写入csv时,会将null转换为“”:
with file_path.open(mode='w', newline='') as outfile:
    csv_writer = csv.writer(outfile, delimiter=',', quoting=csv.QUOTE_NONNUMERIC)
    if include_headers:
        csv_writer.writerow(col[0] for col in self.cursor.description)
    for row in self.cursor:
        csv_writer.writerow(row)
。
42.313270000,-71.116240000
42.377010000,-71.064770000
"",""
空值
指定表示空值的字符串。默认值为文本格式\ N(反斜杠-N),以及CSV格式的无引号的空字符串。对于不想将空值与空字符串区分开的情况,甚至可能以文本格式使用空字符串。使用二进制格式时,不允许使用此选项。
回答:
对我来说解决问题的是将报价更改为csv.QUOTE_MINIMAL。
csv.QUOTE_MINIMAL指示编写器对象仅引用那些包含特殊字符(例如定界符,quotechar或换行符中的任何字符)的字段。
Mar*_*ers 10
这里有两个选项:更改csv.writingPython中的引号选项,或告诉PostgreSQL接受带引号的字符串作为可能的NULL(需要PostgreSQL 9.4或更高版本)。
csv.writer()和报价在Python方面,您正在告诉csv.writer()对象添加引号,因为您已将其配置为使用csv.QUOTE_NONNUMERIC:
指示
writer对象引用所有非数字字段。
None值是非数字的,因此导致""被写入。
切换为使用csv.QUOTE_MINIMAL或csv.QUOTE_NONE:
csv.QUOTE_MINIMAL
指示writer对象仅引用那些包含特殊字符(例如定界符,quotechar或lineterminator中的任何字符)的字段。
csv.QUOTE_NONE
指示writer对象从不引用字段。当前定界符出现在输出数据中时,其前面是当前转义符。
由于您所编写的只是经度和纬度值,因此此处不需要任何引用,因此数据中不存在定界符或引号字符。
无论使用哪个选项,None值的CSV输出都是简单的空字符串:
>>> import csv
>>> from io import StringIO
>>> def test_csv_writing(rows, quoting):
...     outfile = StringIO()
...     csv_writer = csv.writer(outfile, delimiter=',', quoting=quoting)
...     csv_writer.writerows(rows)
...     return outfile.getvalue()
...
>>> rows = [
...     [42.313270000, -71.116240000],
...     [42.377010000, -71.064770000],
...     [None, None],
... ]
>>> print(test_csv_writing(rows, csv.QUOTE_NONNUMERIC))
42.31327,-71.11624
42.37701,-71.06477
"",""
>>> print(test_csv_writing(rows, csv.QUOTE_MINIMAL))
42.31327,-71.11624
42.37701,-71.06477
,
>>> print(test_csv_writing(rows, csv.QUOTE_NONE))
42.31327,-71.11624
42.37701,-71.06477
,
COPY FROM,NULL值和FORCE_NULL从PostgreSQL 9.4开始,NULL使用该FORCE_NULL选项时,还可以强制PostgreSQL接受带引号的空字符串作为s 。从COPY FROM文档中:
FORCE_NULL将指定列的值与空字符串进行匹配(即使已将其引号),并且如果找到匹配项,则将值设置为
NULL。在默认情况下,如果空字符串为空,这会将带引号的空字符串转换为NULL。COPY FROM仅当使用CSV格式时,才允许使用此选项。
在命名列FORCE_NULL选项让PostgreSQL的同时接受空列,并""为NULL这些列,例如值:
>>> import csv
>>> from io import StringIO
>>> def test_csv_writing(rows, quoting):
...     outfile = StringIO()
...     csv_writer = csv.writer(outfile, delimiter=',', quoting=quoting)
...     csv_writer.writerows(rows)
...     return outfile.getvalue()
...
>>> rows = [
...     [42.313270000, -71.116240000],
...     [42.377010000, -71.064770000],
...     [None, None],
... ]
>>> print(test_csv_writing(rows, csv.QUOTE_NONNUMERIC))
42.31327,-71.11624
42.37701,-71.06477
"",""
>>> print(test_csv_writing(rows, csv.QUOTE_MINIMAL))
42.31327,-71.11624
42.37701,-71.06477
,
>>> print(test_csv_writing(rows, csv.QUOTE_NONE))
42.31327,-71.11624
42.37701,-71.06477
,
在那一点上,您在Python端使用了什么引用选项都不再重要。
如果您已经在查询数据库以整理数据以输入PostgreSQL,请考虑直接插入Postgres中。如果数据来自其他来源,则可以使用外部数据包装器(fdw)模块切出中间人并直接将数据从其他来源提取到PostgreSQL中。
Numpy数据可以通过二进制文件COPY FROM更有效地插入;链接的答案使用所需的额外元数据和字节顺序扩充了一个numpy结构化数组,然后有效地创建数据的二进制副本,并使用COPY FROM STDIN WITH BINARY和psycopg2.copy_expert()方法将其插入到PostgreSQL中。巧妙地避免了数字->文本->数字转换。
不要重新发明数据流水线。考虑使用诸如Apache Spark之类的现有项目,这些项目已经解决了效率问题。Spark使您可以将数据视为结构化流,并包括并行运行数据分析步骤的基础结构,并且可以将分布式结构化数据视为Pandas数据帧。
另一种选择是查看Dask,以帮助在分布式任务之间共享数据集以处理大量数据。
即使将一个已经在运行的项目转换为Spark可能距离太远,至少要考虑使用Apache Arrow,Spark就是基于此构建的数据交换平台。该pyarrow项目将允许您通过Parquet文件交换数据,或通过IPC交换数据。
Pandas和Numpy团队投入了大量资金来支持Arrow和Dask的需求(这些项目之间的核心成员有相当多的重叠),并且正在积极努力使Python数据交换尽可能高效,包括扩展Python pickle模块以允许带外数据流,以避免共享数据时不必要的内存复制。
| 归档时间: | 
 | 
| 查看次数: | 2492 次 | 
| 最近记录: |