【问题标题】:numpy.loadtxt: how to ignore comma delimiters that appear inside quotes?numpy.loadtxt:如何忽略引号内出现的逗号分隔符?
【发布时间】:2015-04-11 05:29:55
【问题描述】:

我有一个 csv 文件,其中一行数据可能如下所示:

10,"苹果、香蕉",20,...

当我在 Python 中加载数据时,引号内的额外逗号会移动我所有的列索引,因此我的数据不再是一致的结构。虽然我可能会编写一个复杂的算法来遍历每一行并解决问题,但我希望有一种优雅的方法可以将一个额外的参数传递给 loadtxt (或其他一些函数),它可以正确地忽略引号内的逗号并处理整个引用作为一个值。

注意,当我将 CSV 文件加载到 Excel 中时,Excel 会正确地将字符串识别为一个值。

【问题讨论】:

  • 你试过用python的csv模块加载吗?

标签: python csv numpy


【解决方案1】:

使用单个 numpy 函数调用可以做到这一点的一种方法是使用 np.fromregex,它允许您使用 Python 的 regular expression syntax 以任意方式解析文本文件的内容。例如:

np.fromregex('tmp.csv', r'(\d+),"(.+)",(\d+)', np.object)

给你:

array([['10', 'Apple, Banana', '20'],
       ['30', 'Orange, Watermelon', '40']], dtype=object)

为了稍微解开这个正则表达式,'(\d+)' 将匹配一个或多个数字,'"(.+)"' 将匹配一个或多个双引号内的任何字符。 np.fromregex 尝试在您的 .csv 文件的每一行中匹配此表达式,括号内的部分被视为输出数组每一行中的单个元素。

如果您想要一个记录数组作为您的.csv 文件中三个“列”的不同“字段”的输出,您可以为正则表达式中的每组括号指定单独的dtypes

np.fromregex('tmp.csv', r'(\d+),"(.+)",(\d+)', 'i8, S20, i8')

给你:

array([(10, 'Apple, Banana', 20), (30, 'Orange, Watermelon', 40)], 
      dtype=[('f0', '<i8'), ('f1', 'S20'), ('f2', '<i8')])

【讨论】:

    【解决方案2】:

    这个问题之前已经讨论过了。 loadtxt(或genfromtxt)中没有参数可以满足您的要求。换句话说,它对引用不敏感。 python csv 模块具有某种引用意识。 pandas 的读者也知道引用。

    但是在将它们传递给loadtxt 之前处理这些行是完全可以接受的。所有需要的功能都是一个可迭代的 - 可以一次提供一条线的东西。所以它可以是一个文件、一个行列表或生成器。

    一个简单的处理器只会用其他字符替换引号内的逗号。或者用您选择的分隔符替换引号之外的那些。做这项工作不必花哨。

    Using numpy.genfromtxt to read a csv file with strings containing commas

    例如:

    txt = """10,"Apple, Banana",20
    30,"Pear, Orange",40
    50,"Peach, Mango",60
    """
    
    def foo(astr):
        # replace , outside quotes with ;
        # a bit crude and specialized
        x = astr.split('"')
        return ';'.join([i.strip(',') for i in x]) 
    
    txt1 = [foo(astr) for astr in txt.splitlines()]
    txtgen = (foo(astr) for astr in txt.splitlines())  # or as generator
    # ['10;Apple, Banana;20', '30;Pear, Orange;40', '50;Peach, Mango;60']
    np.genfromtxt(txtgen, delimiter=';', dtype=None)
    

    产生:

    array([(10, 'Apple, Banana', 20), (30, 'Pear, Orange', 40),
           (50, 'Peach, Mango', 60)], 
          dtype=[('f0', '<i4'), ('f1', 'S13'), ('f2', '<i4')])
    

    我之前没有关注np.fromregex。与genfromtxt 相比,它非常简单。要与我的示例 txt 一起使用,我必须使用字符串缓冲区:

    s=StringIO.StringIO(txt)
    np.fromregex(s, r'(\d+),"(.+)",(\d+)', dtype='i4,S20,i4')
    

    它的作用归结为:

    pat=re.compile(r'(\d+),"(.+)",(\d+)'); dt=np.dtype('i4,S20,i4')
    np.array(pat.findall(txt),dtype=dt)
    

    它会读取整个文件 (f.read()) 并执行 findall,它应该会生成如下列表:

    [('10', 'Apple, Banana', '20'),
     ('30', 'Pear, Orange', '40'),
     ('50', 'Peach, Mango', '60')]
    

    元组列表正是结构化数组所需要的。

    没有花哨的处理、错误检查或评论行过滤。只是模式匹配,然后是数组构造。


    我的foofromregex 都假设一个特定的数字序列和带引号的字符串。 csv.reader 可能是最简单的通用报价阅读器。 join 是必需的,因为 reader 生成一个列表列表,而 genfromtxt 想要一个可迭代的字符串(它自己进行“拆分”)。

    from csv import reader
    s=StringIO.StringIO(txt)
    np.genfromtxt((';'.join(x) for x in reader(s)), delimiter=';', dtype=None)
    

    生产

    array([(10, 'Apple, Banana', 20), (30, 'Pear, Orange', 40),
           (50, 'Peach, Mango', 60)], 
          dtype=[('f0', '<i4'), ('f1', 'S13'), ('f2', '<i4')])
    

    或者按照fromregex的例子,reader的输出可以变成一个元组列表,直接给np.array

    np.array([tuple(x) for x in reader(s)], dtype='i4,S20,i4')
    

    【讨论】:

      【解决方案3】:

      我使用下面的代码解决了这个问题。

      def transformCommas(line):
          out = ''
          insideQuote = False
          for c in line:
              if c == '"':
                  insideQuote = not insideQuote
              if insideQuote == True and c == ',':
                  out += '.'
              else:
                  out += c
          return out
      
      f = open("data/raw_data_all.csv", "rb")
      replaced = (transformCommas(line) for line in f)
      rawData = numpy.loadtxt(replaced,delimiter=',', skiprows=0, dtype=str)
      

      数据:

      1366x768,18,"5,237",73.38%,"3,843",79.55%,1.75,00:01:26,4.09%,214,$0.00
      1366x768,22,"5,088",76.04%,"3,869",78.46%,1.82,00:01:20,3.93%,200,$0.00
      1366x768,17,"4,887",74.34%,"3,633",78.37%,1.81,00:01:19,3.25%,159,$0.00
      

      【讨论】:

        【解决方案4】:

        您可以使用 Python csv 模块:https://docs.python.org/2/library/csv.html

        给定一个 csv 格式的数据文件:

        10,"Apple,Banana",20
        20,"Orange,Watermelon",30
        

        使用此脚本:

        from csv import reader
        
        with open('data.csv') as f:
            for row in reader(f):
                print row
        

        你得到:

        ['10', 'Apple,Banana', '20']
        ['20', 'Orange,Watermelon', '30']
        

        由于 loadtxt 需要一个可迭代对象,因此将其传递给reader(f)

        with open('data.csv') as f:
            data = loadtxt(reader(f), ...)
        

        【讨论】:

        • loadtxt(reader(f)...) 不起作用,因为loadtxt 想要一个返回字符串的迭代。 reader() 生成列表列表。你需要join他们:(';'.join(x) for x in reader(f))
        【解决方案5】:

        虽然 numpy.loadtxt 中没有这样的参数来忽略带引号或以其他方式转义的逗号,但尚未建议的一种替代方法是以下...

        使用一些文本编辑器执行查找和替换,将逗号替换为制表符 要么 将文件保存在 Excel 中以制表符分隔。

        当您使用 numpy.loadtxt 时,您只需指定 delimiter='\t' 而不是逗号分隔。

        可以为您节省一些代码的简单解决方案...

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-12-17
          • 2018-06-30
          • 2019-12-25
          • 2019-03-28
          • 1970-01-01
          • 2012-11-19
          相关资源
          最近更新 更多