这与Get pandas.read_csv to read empty values as empty string instead of nan
相反给出以下CSV文件:
col,val
"hi
there",1
,2
\f\,3
"",4
"""hi""",5
我希望它读为:
col val
0 hi\nthere 1
1 NaN 2
2 \f\ 3
3 4
4 "hi" 5
也就是说,将空字段(值2)读取为NaN
,同时将空字符串(值4)保留为空字符串。
当前pd.read_csv
将val 2和val 4都转换为NaN
,或者如果我使用na_filter=False
都将其保留为空字符串。
我假设这两种表示形式在CSV中表示不同的内容(空字段与空字符串),所以我假设熊猫也应该能够区分这两种情况。
有没有办法让熊猫区分这两种情况?还是我的假设是错误的,即两种表示形式实际上是相同的? (如果是第二种情况,请给我指出CSV标准)
更多信息,我通过将BigQuery表(具有预期的含义,val 2为空,而val 4为空字符串)导出到CSV中而获得了CSV。我想找回完全相同的表。 因此,此示例不仅是人为的示例,而且在导出为CSV时,BigQuery实际使用了该示例。
编辑:进一步的搜索显示Github issue 4 years ago讨论了类似的观点(例如,参见this comment),其中一位评论者提到存在某种强制性(我不确定他们指的是什么) ,但我理解为空字段和空字符串之间的强制。这还在发生吗?
答案 0 :(得分:3)
另一个选择是禁用引号,以获取存在空字符串且不存在任何内容的字段。这种情况下的问题是测试中包含换行符的条目。我们需要先删除这些字符,然后合并各行以创建新的数据文件。
在带引号的情况下读取新数据文件时,空值为NaN,空字符串为两个引号。然后,可以使用此数据框设置原始数据框中的NaN,以设置实际的NaN。
import numpy as np
import pandas as pd
with open('./data.csv') as f:
lines = f.readlines()
# merge lines where the comma is missing
it = iter(lines)
lines2 = [x if ',' in x else x + next(it) for x in it]
# replace \n which are not at the end of the line
lines3 = [l.replace('\n','') + '\n' for l in lines2]
# write new file with merged lines
with open('./data_merged.csv', 'w+') as f:
f.writelines(lines3)
# read original data
df = pd.read_csv('./data.csv', na_filter=False)
# read merged lines data with quoting off
df_merged = pd.read_csv('./data_merged.csv', quoting=3)
# in df_merged dataframe if is NaN it is a real NaN
# set lines in original df to NaN when in df_merged is NaN
df.loc[df_merged.col.isna(), 'col'] = np.NaN
答案 1 :(得分:3)
pandas.read_csv接受一个quoting
参数,该参数控制每个字段的引用行为。该参数接受类型为int
或csv.QUOTE_*
的值。后者是csv模块中定义的常量。在所有可用选项中,需要注意的一个是csv.QUOTE_NONE。该常量指示reader对象不对引号字符进行特殊处理,这意味着双引号中的字段将按原样读取,并且在解析时不会在字段中添加其他双引号。熊猫设置的默认值为csv.QUOTE_MINIMAL。
In [237]: import csv
In [238]: import pandas as pd
In [239]: df = pd.read_csv("test.csv", quoting=csv.QUOTE_NONE)
In [240]: df
Out[240]:
col val
0 "hi NaN
1 there" 1.0
2 NaN 2.0
3 \f\ 3.0
4 "" 4.0
5 """hi""" 5.0
在没有特殊引号的情况下,将空值解析为NaN,并保留带有双引号的空字符串。
但是这种方法存在一个问题:如果任何字段在双引号中包含换行符,则将它们视为单独的字符串。这在csv文件的第一行中很明显,其中“ hi \ nthere”是由熊猫在单独的行中解析的。为了解决这个问题,我首先使用re
模块进行了一些预处理。这是将双引号字符串中的所有换行符替换为whitepace所必需的。然后,我写回了相同的文件,并像上面的read_csv
一样再次使用它。由于我不知道您的数据的完整格式,因此可能需要更多的正则表达式。但是,对于给定的问题,我得到了所需的输出。
In [314]: with open("test.csv", 'r+') as f:
...: data = f.read()
...: import re
...: pattern = re.compile(r'".*?"', re.DOTALL)
...: data = pattern.sub(lambda x: x.group().replace('\n', ' '), data)
...: f.seek(0)
...: f.write(data)
In [315]: df = pd.read_csv("test.csv", quoting=csv.QUOTE_NONE)
In [316]: df
Out[316]:
col val
0 "hi there" 1
1 NaN 2
2 \f\ 3
3 "" 4
4 """hi""" 5
答案 2 :(得分:1)
这里有些丑陋但完整的答案:
import io
import re
import pandas as pd
with open('overflow.csv', 'r') as f:
with io.StringIO(re.sub(r'(^"",)', "EMPTY_STR,", f.read(), flags=re.MULTILINE)) as ff:
with io.StringIO(re.sub(r'(,"",)', ",EMPTY_STR,", ff.read(), flags=re.MULTILINE)) as fff:
with io.StringIO(re.sub(r'(,""$)', ",EMPTY_STR", fff.read(), flags=re.MULTILINE)) as ffff:
df = pd.read_csv(ffff)
df= df.replace('EMPTY_STR', '')
re.sub()
用EMPTY_STR
替换空字符串,以后可以用实际的空字符串替换。对于所有三种可能的事件类型(行的开头,中间和行),必须调用三次。
真正的空单元格被搁置,并且确实被解释为NaN
。
答案 3 :(得分:0)
在创建BigQuery csv导出时,是否有任何其他方法可以将空字符串替换为其他字符串?像将""
替换为"EMPTY_STR"
一样?然后,您可以在使用.read_csv()
时使用转换器函数将其替换为空字符串。