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
会起作用
使用以下 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
会起作用