在 python 中使用 csv.DictReader 进行数据类型转换的最快方法

Posted

技术标签:

【中文标题】在 python 中使用 csv.DictReader 进行数据类型转换的最快方法【英文标题】:Fastest way to do data type conversion using csv.DictReader in python 【发布时间】:2011-06-21 08:11:16 【问题描述】:

我正在使用 python 中的 CSV 文件,使用时将有大约 100,000 行。每行都有一组维度(作为字符串)和一个指标(浮点数)。

由于 csv.DictReader 或 csv.reader 仅将值作为字符串返回,我目前正在遍历所有行并将一个数值转换为浮点数。

for i in csvDict:
    i[col] = float(i[col])

有没有人可以建议这样做的更好方法?我一直在玩各种 map、izip、itertools 的组合,并广泛搜索了一些更有效的示例,但不幸的是没有取得太大的成功。

如果有帮助: 我在 appengine 上做这个。我相信我正在做的事情可能会导致我遇到这个错误: 在总共处理 11 个请求后,超过了 267.789 MB 的软进程大小限制 - 我只有在 CSV 非常大时才得到它。

编辑:我的目标 我正在解析这个 CSV,以便我可以将其用作data source for the Google Visualizations API。最终的数据集将被加载到 gviz DataTable 中进行查询。在构建此表期间必须指定类型。如果有人知道python中有一个好的gviz csv->datatable转换器,我的问题也可以解决!

Edit2:我的代码

我相信我的问题与我尝试修复 CsvTypes() 的方式有关。此外,data_table.LoadData() 需要一个可迭代的对象。

class GvizFromCsv(object):
  """Convert CSV to Gviz ready objects."""

  def __init__(self, csvFile, dateTimeFormat=None):
    self.fileObj = StringIO.StringIO(csvFile)
    self.csvDict = list(csv.DictReader(self.fileObj))
    self.dateTimeFormat = dateTimeFormat
    self.headers = 
    self.ParseHeaders()
    self.fixCsvTypes()

  def IsNumber(self, st):
    try:
        float(st)
        return True
    except ValueError:
        return False

  def IsDate(self, st):
    try:
      datetime.datetime.strptime(st, self.dateTimeFormat)
    except ValueError:
      return False

  def ParseHeaders(self):
    """Attempts to figure out header types for gviz, based on first row"""
    for k, v in self.csvDict[0].items():
      if self.IsNumber(v):
        self.headers[k] = 'number'
      elif self.dateTimeFormat and self.IsDate(v):
        self.headers[k] = 'date'
      else:
        self.headers[k] = 'string'

  def fixCsvTypes(self):
    """Only fixes numbers."""
    update_to_numbers = []
    for k,v in self.headers.items():
      if v == 'number':
        update_to_numbers.append(k)
    for i in self.csvDict:
      for col in update_to_numbers:
        i[col] = float(i[col])

  def CreateDataTable(self):
    """creates a gviz data table"""
    data_table = gviz_api.DataTable(self.headers)
    data_table.LoadData(self.csvDict)
    return data_table

【问题讨论】:

您如何处理这些数据?逐行处理还是将它们放入某种数据结构中? 除了固定类型之外,我不打算进行其他线路处理。结果将用作构建 gviz DataTable 的数据 - 我现在已将其添加到问题中! 【参考方案1】:

我首先用正则表达式来利用CSV文件,但是由于文件中的数据在每一行中排列非常严格,我们可以简单地使用split()函数

import gviz_api

scheme = [('col1','string','SURNAME'),('col2','number','ONE'),('col3','number','TWO')]
data_table = gviz_api.DataTable(scheme)

#  --- lines in surnames.csv are : --- 
#  surname,percent,cumulative percent,rank\n
#  SMITH,1.006,1.006,1,\n
#  JOHNSON,0.810,1.816,2,\n
#  WILLIAMS,0.699,2.515,3,\n

with open('surnames.csv') as f:

    def transf(surname,x,y):
        return (surname,float(x),float(y))

    f.readline()
    # to skip the first line surname,percent,cumulative percent,rank\n

    data_table.LoadData( transf(*line.split(',')[0:3]) for line in f )
    # to populate the data table by iterating in the CSV file

或者没有要定义的函数:

import gviz_api

scheme = [('col1','string','SURNAME'),('col2','number','ONE'),('col3','number','TWO')]
data_table = gviz_api.DataTable(scheme)

#  --- lines in surnames.csv are : --- 
#  surname,percent,cumulative percent,rank\n
#  SMITH,1.006,1.006,1,\n
#  JOHNSON,0.810,1.816,2,\n
#  WILLIAMS,0.699,2.515,3,\n

with open('surnames.csv') as f:

    f.readline()
    # to skip the first line surname,percent,cumulative percent,rank\n

    datdata_table.LoadData( [el if n==0 else float(el) for n,el in enumerate(line.split(',')[0:3])] for line in f )    
    # to populate the data table by iterating in the CSV file

有一次,我认为我必须一次用一行填充数据表,因为我使用的是正则表达式,并且需要在浮动数字字符串之前获取匹配的组。使用 split() 可以在一条指令中使用 LoadData()

完成所有操作

.

因此,您的代码可以缩短。顺便说一句,我不明白为什么它应该继续定义一个类。相反,一个函数对我来说似乎就足够了:

def GvizFromCsv(filename):
  """ creates a gviz data table from a CSV file """

  data_table = gviz_api.DataTable([('col1','string','SURNAME'),
                                   ('col2','number','ONE'    ),
                                   ('col3','number','TWO'    ) ])

  #  --- with such a table schema , lines in the file must be like that: ---  
  #  blah, number, number, ...anything else...\n 
  #  SMITH,1.006,1.006, ...anything else...\n 
  #  JOHNSON,0.810,1.816, ...anything else...\n 
  #  WILLIAMS,0.699,2.515, ...anything else...\n

  with open(filename) as f:
    data_table.LoadData( [el if n==0 else float(el) for n,el in enumerate(line.split(',')[0:3])]
                         for line in f )
  return data_table

.

现在您必须检查是否可以在此代码中插入从另一个 API 读取 CSV 数据的方式,以保持填充数据表的迭代原则。

【讨论】:

谢谢!为了完整起见,我在此垫上包含了两种解决方案:piratepad.net/fJfKE2Juyl 我需要对您的代码进行的唯一次要考虑是需要自动处理列类型标识(理想情况下,此代码应该能够解释任何csv 没有用户指示),假设用户还不知道它。另外 - 我做了一些基本的时间比较 - 类和你的函数在 80,000 行中都花费了大约 6 秒,而你在 20 次运行中以微弱的优势获胜。 @oli float('45.89\n') 不会出错,但 datetime.datetime.strptime('2014 4 28\n','%Y %m %d') 会出错。所以你应该写 [getConvType(el.strip('\n'))[0] for el in f.next()] 而不是 [getConvType(el)[0] for el in f.next()],并且与 [getConvType(el)[1] for el in f.next()] 相同,以防每行的最后一个位置有一个日期时间字符串。 @oli 但其实最好写成 [el for el in f.readline()[0:-1].split(',')] 写成 [getConvType( el)[1] for el in f.next()[0:-1].split(',')] 并为 [getConvType(el)[0] for el in line[0:-1].split (',')] 。关键是添加 [0:-1] 。如果启用 Universal Newline Support(这是默认设置),它将始终有效。 @oli 我在某些方面改进了代码,但遇到了一个我不明白的问题。当我能够调试时,您将不得不观看阅读。【参考方案2】:

首先,如果您需要可视化这些数据,您不需要任何转换:gviz 可以处理 JSON(基于文本,你知道)或 CSV(你已经有了它,不需要解析!)。您可以将有问题的文件放在任何合理的 Web 服务器上,并允许通过花哨的 GET 请求 gviz 问题访问它,基本上是通过忽略参数。

但是让我们假设您需要处理。看起来您不仅读取了 CSV 文件,还尝试将其完全存储在 RAM 中。这可能是不切实际的:随着您添加更多处理,您将越来越快地达到 RAM 限制。一次处理一行数据(如果应用窗口过滤器等,则处理合理数量的行)并将处理后的行放入数据存储,而不是任何列表等。同样,当通过 GET 请求提供数据时,读取 /处理一行,将其写入响应,不要将其放入任何列表或诸如此类的东西中。

我认为转换技术没有问题,只要你在代码中合理地使用i,并且不要在你去的时候记住所有is。

【讨论】:

非常有帮助的回复,非常感谢。一些注意事项:1)我做这一切的唯一原因是因为我认为我需要在将 CSV 发送到 gviz 之前对其进行处理。我不认为 gviz 将 CSV 文件作为 DS 处理。我目前正在尝试做:csv->DataTable.toJsonResponse()。从你所说的来看,我可能根本不需要那样做——现在就去玩。我受到限制,因为 CSV 必须 通过 python 输入,但我很乐意将它作为 gviz 数据源推出。 2)如果我使用 CSV 作为数据源,并在此之上执行查询,它会找出类型(用于求和等)吗? 我不知道我是否可以将 CSV 与 gviz 一起使用 - groups.google.com/group/google-visualization-api/browse_thread/… 如果您有不同的认识,可以告诉我吗? 点击您发布的链接:code.google.com/apis/visualization/documentation/dev/… 在链接的段落“Java 和 Python 开源库提供了一种将 DataTable 转换为 CSV 字符串的方法”。 oli 想反其道而行之:“我的目标是解析这个 CSV,以便我可以将其用作 Google Visualizations API 的数据源。”【参考方案3】:

有两个不同的东西: “数据源”和“数据表”。

“数据源”是由 Google 可视化 API 服务器作为可视化 Web 服务提供的格式化数据的名称:

This page describes how you can implement a data source to feed data
to visualizations built on the Google Visualization API. 

http://code.google.com/intl/fr/apis/visualization/documentation/dev/implementing_data_source.html 

“数据源”的名称包括“有线协议”的概念:

In response [to a request], the data source returns properly formatted data 
that the visualization can use to render the graphic on the page. 
This request-response protocol is known as the Google Visualization API wire protocol,

http://code.google.com/intl/fr/apis/visualization/documentation/dev/implementing_data_source_overview.html

要实现“数据源”,有两种可能:

• Use one of the data source libraries listed in the Data Sources and Tools Gallery. 
All the data source libraries listed on that page implement the wire protocol.

• Write your own data source from scratch, 

http://code.google.com/intl/fr/apis/visualization/documentation/dev/implementing_data_source_overview.html

来自以下:

• ... Data Sources and Tools Gallery : (....) You therefore need write only the
code needed to make your data available to the library in the form of a data table. 

• Write your own data source from scratch, as described in the
Writing your own Data Source

我明白,从头开始,我们需要自己实现有线协议+创建“数据表”,而使用数据源库,我们只需要创建“数据表”。


有关于创建“数据源”的页面

http://code.google.com/intl/fr/apis/visualization/documentation/dev/implementing_data_source_overview.html

http://code.google.com/intl/fr/apis/visualization/documentation/dev/gviz_api_lib.html

在我看来,地址http://groups.google.com/group/google-visualization-api/browse_thread/thread/9d1d941e0f0b32ed 的示例是关于创建“数据源”的,而那里的答案是可疑的。但这对我来说不是很清楚。


但是这些页面和主题对你​​来说不是有趣的,事实上,如果我理解得很好,他们想知道如何准备数据,称为“数据表”,通过“数据源”提供服务" ,但不是“数据源”的构造。

3.Prepare your data. You'll need to prepare the data to visualize; 
this means either specifying the data yourself in code, 
or querying a remote site for data.

http://code.google.com/intl/fr/apis/visualization/documentation/using_overview.html#keycomponents

A visualization stores the data that it visualizes as two-dimensional data table with 
rows and columns.
Cells are referenced by (row, column) where row is a zero-based row number, and column
is either a zero-based column index or a unique ID that you can specify. 

http://code.google.com/intl/fr/apis/visualization/documentation/using_overview.html#preparedata

所以,“数据表”的准备是重点。

这里是:

There are two ways to create/populate your visualization's data table:

•Query a data provider. A data provider is another site that returns
a populated DataTable in response to a request from your code. 
Some data providers also accept SQL-like query strings to sort or 
filter the data. See Data Queries for more information and an example
of a query.

•Create and populate your own DataTable by hand. You can populate your
DataTable in code on your page. The simplest way to do this is to create
a DataTable object without any data and populate it by calling addRows()
on it. You can also pass a javascript literal representation of the data
table into the DataTable constructor, but this is more complex and is
covered on the reference page.

http://code.google.com/intl/fr/apis/visualization/documentation/using_overview.html#preparedata

更多信息可在此处找到:

2. Describe your table schema
The table schema is specified by the table_description parameter
passed into the constructor. You cannot change it later. 
The schema describes all the columns in the table: the data type of
each column, the ID, and an optional label.

Each column is described by a tuple: (ID [,data_type [,label [,custom_properties]]]). 



The table schema is a collection of column descriptor tuples. 
Every list member, dictionary key or dictionary value must be either 
another collection or a descriptor tuple. You can use any combination 
of dictionaries or lists, but every key, value, or member must
eventually evaluate to a descriptor tuple. Here are some examples.

•List of columns: [('a', 'number'), ('b', 'string')]
•Dictionary of lists: ('a', 'number'): [('b', 'number'), ('c', 'string')]
•Dictionary of dictionaries: ('a', 'number'): 'b': 'number', 'c': 'string'
•And so on, with any level of nesting.


3. Populate your data
To add data to the table, build a structure of data elements in the
exact same structure as the table schema. So, for example, if your
schema is a list, the data must be a list: 

•schema: [("color", "string"), ("shape", "string")] 
•data: [["blue", "square"], ["red", "circle"]] 
If the schema is a dictionary, the data must be a dictionary:

•schema: ("rowname", "string"): [("color", "string"), ("shape", "string")] 
•data: "row1": ["blue", "square"], "row2": ["red", "circle"]

http://code.google.com/intl/fr/apis/visualization/documentation/dev/gviz_api_lib.html#populatedata

最后,我想说,对于您的问题,您必须定义一个“表架构”并处理您的 CSV 文件以获得a structure of data elements in the exact same structure as the table schema.

列中数据类型的定义在“表模式”的定义中完成。如果必须使用具有正确类型的数据(不是字符串,我想说)填充“数据表”,我将帮助您编写从 CSV 中提取数据的代码,这很简单。

目前,我希望这一切都是正确的,并且会有所帮助

【讨论】:

Eyquem,非常感谢您抽出宝贵时间回复。我已经在帖子中包含了我的代码;它打算将 CSV 转换为 python 中的 DataTable 对象。一旦数据采用正确的格式,到 DataTable 的实际转换将由 gviz_api 处理(在 CreateDataTable() 中处理)。我打算将其作为数据源公开。正如@9000 所提到的,我相信问题在于 fixCsvTypes() 尝试遍历所有条目并将适当的列转换为数字(或者,在未来的日期时间等),并在此过程中保存它们。也许 self.csvDict 最终太大了? @oli 谢谢。也许对你已经知道的点有太多的解释。但它让我学习了这个主题!我刚刚阅读了你的代码。 “另外,data_table.LoadData() 需要一个可迭代的对象。”完美的。这将允许迭代到 self.csvDict 以填充 DataTable。但是我缺少一些东西:现在 csvFile 在哪里,它将被提供给 GvizFromCsv 实例?我认为不需要使用 StringIO.StringIO 处理文件。后者用于将字符串作为文件读取;但你已经有一个文件。我不理解引入 StringIO 的动机 @Eyquem 希望我们都能从中得到一些东西 :)。要点:1)CSV字符串实际上来自另一个API,格式为:“Col1,Col2,Col3\nHi,There,123”。出于某种原因, StringIO.StringIO 是我可以正确解析它的唯一方法(否则它会在每个字符处拆分我的 CSV 字符串,而不是逗号)。 2) csv.Dict 可以填充 DataTable 很有用,但我的问题是我需要在执行此操作之前将 csv.Dict 每一行中的条目从字符串转换为浮点数。我正在调查 gviz_api.AppendData (一次追加一行),这可能会有所帮助。 @oli “我的问题是我需要将 csv.Dict 每一行中的条目从字符串转换为浮点数”是的,我明白这一点。但是不,我不认为它必须按照你输入的方式来完成。这就是为什么我收集有关该主题的信息和知识来按照我的想法去做。你给了我“Col1,Col2,Col3\nHi,There,123”,这很好继续。我刚刚成功下载并解压缩了“google-visualization-python”库。稍后我会建议您进行一些修改。 @oli 你能给我 csvDict[0] 的值吗?我看到 ParseHeaders() 使用此值返回填充了表模式的 self.headers,该模式被传递给 gviz_api.DataTable() 以实例化 DataTable

以上是关于在 python 中使用 csv.DictReader 进行数据类型转换的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

当python使用“Python.h”调用该c++进程时,如何在python中停止一个c++进程

在 Python 中使用 Weka 生成的决策树

在 python 中使用 soffice,Command 在终端中有效,但在 Python 子进程中无效

python 使用pymongo在python中使用MongoDB的示例

在 python 中使用命令行时出现语法错误

python 在python中使用全局变量