【问题标题】:Reading CSV files in numpy where delimiter is ","在分隔符为“,”的numpy中读取CSV文件
【发布时间】:2011-02-09 12:33:57
【问题描述】:

我有一个格式如下的 CSV 文件:

“字段名称 1”、“字段名称 2”、“字段名称 3”、“字段名称 4”
“2010 年 4 月 13 日 14:45:07.008”、“7.59484916392”、“10”、“6.552373”
“2010 年 4 月 13 日 14:45:22.010”、“6.55478493312”、“9”、“3.5378543”
...

请注意,CSV 文件中每行的开头和结尾都有双引号字符,并且"," 字符串用于分隔每行内的字段。 CSV 文件中的字段数可能因文件而异。

当我尝试通过以下方式将其读入 numpy 时:
import numpy as np
data = np.genfromtxt(csvfile, dtype=None, delimiter=',', names=True)
所有数据都以字符串值的形式读入,并用双引号字符括起来。不是不合理,但对我没有多大用处,因为我必须返回并将每一列转换为正确的类型

当我改用delimiter='","' 时,一切都按我的意愿进行,除了 用于第一个和最后一个字段。由于行首和行尾字符是单个双引号字符,因此这不被视为第一个和最后一个字段的有效分隔符,因此它们被读取为例如"04/13/2010 14:45:07.0086.552373" - 分别注意前导和尾随双引号字符。由于这些冗余字符,numpy 假设第一个和最后一个字段都是 String 类型;我不希望出现这种情况

有没有一种方法可以指示 numpy 读取以这种方式格式化的文件,而无需在初始读取后返回并“修复”numpy 数组的结构?

【问题讨论】:

    标签: python csv numpy delimiter


    【解决方案1】:

    基本问题是 NumPy 不理解去除引号的概念(而 csv 模块可以)。当您说delimiter='","' 时,您是在告诉 NumPy 列分隔符实际上是带引号的逗号,即引号在​​逗号周围,而不是值,因此您在第一列和最后一列上得到的额外引号是预期的。

    查看函数文档,我认为您需要设置 converters 参数来为您去除引号(默认情况下不会):

    import re
    import numpy as np
    
    fieldFilter = re.compile(r'^"?([^"]*)"?$')
    def filterTheField(s):
        m = fieldFilter.match(s.strip())
        if m:
            return float(m.group(1))
        else:
            return 0.0 # or whatever default
    
    #...
    
    # Yes, sorry, you have to know the number of columns, since the NumPy docs
    # don't say you can specify a default converter for all columns.
    convs = dict((col, filterTheField) for col in range(numColumns))
    data = np.genfromtxt(csvfile, dtype=None, delimiter=',', names=True, 
        converters=convs)
    

    或者放弃np.genfromtxt(),让csv.csvreader一次给你一行文件内容,作为字符串列表,然后你只需遍历元素并构建矩阵:

    reader = csv.csvreader(csvfile)
    result = np.array([[float(col) for col in row] for row in reader])
    # BTW, column headings are in reader.fieldnames at this point.
    

    编辑:好的,看起来您的文件并非都是浮动的。在这种情况下,您可以在genfromtxt 情况下根据需要设置convs,或者在csv.csvreader 情况下创建转换函数向量:

    reader = csv.csvreader(csvfile)
    converters = [datetime, float, int, float]
    result = np.array([[conv(col) for col, conv in zip(row, converters)] 
        for row in reader])
    # BTW, column headings are in reader.fieldnames at this point.
    

    编辑 2:好的,可变列数...您的数据源只是想让生活变得困难。幸运的是,我们可以使用magic...

    reader = csv.csvreader(csvfile)
    result = np.array([[magic(col) for col in row] for row in reader])
    

    ...其中magic() 只是我为一个函数而想到的一个名字。 (心灵!)

    在最坏的情况下,它可能是这样的:

    def magic(s):
        if '/' in s:
            return datetime(s)
        elif '.' in s:
            return float(s)
        else:
            return int(s)
    

    也许 NumPy 有一个函数,它接受一个字符串并返回一个具有正确类型的元素。 numpy.fromstring() 看起来很接近,但它可能会将时间戳中的空格解释为列分隔符。

    附:我看到csvreader 的一个缺点是它不会丢弃 cmets。真正的 csv 文件没有 cmets。

    【讨论】:

    • 如果输入文件很大(许多 MB 或 GB),str.replace('"', '') 方法的执行速度应该明显快于正则表达式,并且如果您可以假设 " 字符不会出现,则将是正确的在场地的中间,只有在末端。
    • 感谢 Mike 和 gotgenes,但我还应该提到 CSV 文件的列数可变。我可能会使用您描述的方法,通过添加一个初始步骤来读取文件的第一条记录以确定列数,然后将其用作后续步骤的输入,但这似乎很笨重。有没有更好的办法?
    • 小提示:你不需要使用re.compile(),因为直接使用re.match()会缓存编译后的正则表达式。
    • @blokeley:真的吗?我从来没有听说过。谢谢...但是我仍然会使用re.compile,因为它可以让我命名我的正则表达式。
    • @Mike DeSimone。确实是的。我希望这有帮助。请参阅docs.python.org/library/re.html#re.compile 处的注释
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-22
    • 1970-01-01
    • 2020-07-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多