Pandas read_csv 不解释 quotechar

Pandas read_csv do not interprete quotechar

使用以下 CSV 文件:

"aa"!#"2811"!#"Location"!#"11"!#"67000"!#"ZZ"
"bb"!#2812!#"Location"!#"22"!#"67540"!#"XX"
"cc"!#"2813"!#Location!#"33"!#"67117"!#"YY"
"dd"!#"2452"!#"location"!#"44"!#"67000"!#"ZZ"

并使用以下 python 代码:

import pandas
import csv
pandas.read_csv("test.csv", sep="!#", header=None, quotechar='"')

给出以下结果:

      0      0       1           2     3        4     5
0  "aa"  "2811"  "Location"  "11"  "67000"  "ZZ"
1  "bb"    2812  "Location"  "22"  "67540"  "XX"
2  "cc"  "2813"    Location  "33"  "67117"  "YY"
3  "dd"  "2452"  "location"  "44"  "67000"  "ZZ"

然而,正如我指定的那样quotechar='"',结果应该是

    0     1         2   3      4   5
0  aa  2811  Location  11  67000  ZZ
1  bb  2812  Location  22  67540  XX
2  cc  2813  Location  33  67117  YY
3  dd  2452  location  44  67000  ZZ

我错过了什么吗?

编辑:, 替换所有 !# 使其工作,所以显然, quotechar 没有被解释为 sep 超过 1 个字符。所以我正在寻找没有任何 str.replace() 的解决方案(我无法更改 !#,并且 " 很重要,因为可以在一列中找到 !#

请看这里:

如果分隔符超过一个字符,quotechar 将不起作用。我试过用逗号作为分隔符,它起作用了。

df = pd.read_csv("test.csv", delimiter="!#", header=None)
df = df.apply(lambda x: x.str.strip('"'))

输出

    0     1         2   3      4   5
0  aa  2811  Location  11  67000  ZZ
1  bb  2812  Location  22  67540  XX
2  cc  2813  Location  33  67117  YY
3  dd  2452  location  44  67000  ZZ

您也可以在读取文件后使用替换功能

data = pd.read_csv('/Users/a70286/Desktop/temp.csv',sep="!#", header=None)
data.apply(lambda x: x.str.replace('"',""))

解决方法如下:

import re

data = []
with open(filename, 'r') as f:
    for row in f:
        data.append([re.sub(r'^"|"$', '', item).strip() for item in row.split('!#')])
df = pd.DataFrame(data)
>>> print(df)
    0     1         2   3      4   5
0  aa  2811  Location  11  67000  ZZ
1  bb  2812  Location  22  67540  XX
2  cc  2813  Location  33  67117  YY
3  dd  2452  location  44  67000  ZZ

为了解释正则表达式,它删除了在每个已解析字符串的开头或结尾找到的双引号。

  • ^" 断言引号在字符串开头的位置。
  • "$ 断言引号在字符串末尾的位置。
  • | 匹配以上任一断言。

[re.sub(r'^"|"$', '', item).strip() for item in row.split('!#')] 是一个列表理解,它从行的每个已解析元素中删除开始和结束引号并去除所有空格。

我们知道使用带 pd.read_csv 的多字符定界符将调用正则表达式引擎,使用正则表达式定界符是 prone to ignoring quoted data —— 至少,我不明白它是怎么回事完成,特别是如果我们希望 !# 在一对引号内时被视为非定界符。

因此,如果我们要使用 pd.read_csv,我们将不得不使用单字符定界符。 会是什么?如果我们使用 sep='!',那么某些列将以 # 开头——这会混淆初始引号的含义。清理引号是一件令人头疼的事情(请注意 X 周围的双引号未正确解析):

In [218]: pd.read_csv(StringIO(content), sep='!', quotechar='"', header=None)
Out[218]: 
      0        1                  2      3         4     5
0  a!#a  #"2811"  #"Location ""X"""  #"11"  #"67000"  #"1"
1    bb    #2812        #"Location"  #"22"  #"67540"  #"2"
2    cc  #"2813"          #Location  #"33"  #"67117"  #"3"
3    dd  #"2452"        #"location"  #"44"  #"67000"  #"4"

因此最好使用sep='#'。所以我们的手基本上是被逼的:我们需要使用sep='#',和quotechar='"':

In [219]: pd.read_csv(StringIO(content), sep='#', quotechar='"', header=None)
Out[219]: 
       0      1              2    3       4  5
0  a!#a!  2811!  Location "X"!  11!  67000!  1
1    bb!  2812!      Location!  22!  67540!  2
2    cc!  2813!      Location!  33!  67117!  3
3    dd!  2452!      location!  44!  67000!  4

仍然,我们需要事后清理 !s,但至少引号得到了适当的尊重(注意 a!#a! 中的 !# 是不解释为定界符)。

下面的代码显示了如何删除 ! 并将数字字符串转换为数字:

import pandas as pd
StringIO = pd.io.common.StringIO

content = '''\
"a!#a"!#"2811"!#"Location ""X"""!#11!!#"67000"!#"1"
"bb"!#2812!#"Location"!#"22"!#"67540"!#"2"
"cc"!#"2813"!#Location!#"33"!#"67117"!#"3"
"dd"!#"2452"!#"location"!#"44"!#"67000"!#"4"
'''
df = pd.read_csv(StringIO(content), sep='#', quotechar='"', header=None)
for col in df.columns[:-1]:
    try: df[col] = df[col].str[:-1]
    except AttributeError: pass
    try: df[col] = pd.to_numeric(df[col], errors='raise')
    except ValueError: pass
print(df)

产量

      0     1             2    3      4  5
0  a!#a  2811  Location "X"  11!  67000  1
1    bb  2812      Location   22  67540  2
2    cc  2813      Location   33  67117  3
3    dd  2452      location   44  67000  4

我稍微修改了您的 "CSV" 数据,以确保引号内的 !# 未被修改 并且双引号被正确解析为单引号。

在 read_csv 中使用 quoting = 3 会起作用