问题描述
我正在读取CSV数据用作熊猫数据框,但是CSV似乎没有遵循任何理智的惯例(除了使用;
作为分隔符之外,每个人都应该...)。看来唯一的目标是使它们在文本编辑器中打开时看起来好
以下是一些示例(设置为变量,以便可以将其用于阅读器示例):
ex1="""
Type: Some Metadata
User: More Metadata
Data:
01.10.1939 00:00:00 ; 1,1 ;
01.12.1939 00:00:00 ; 1 ;
01.01.1940 00:00:00 ; 10 ;
"""
好,十进制逗号(简单),分号分隔符(简单),dayfirst(简单)和一堆元数据(skiprorow,也很简单)。
ts = pd.read_csv(io.StringIO(ex1),skiprows=4,decimal=',',sep=';',index_col=0,usecols=[0,1],dayfirst=True,parse_dates=True,names=['date','val'])
print(ts
产生了一个不错的数据框
val
date
1939-10-01 1.1
1939-12-01 1.0
1940-01-01 10.0
和ts.index
是一个不错的DatetimeIndex
,而type(ts.val[0])
是一个numpy.float64
。但是,让我们介绍一种标记NaN
的创意方法:
ex2="""
Type: Some Metadata
User: More Metadata
Data:
01.10.1939 00:00:00 ; 1,1;
01.12.1939 00:00:00 ; NÄ ;
01.01.1940 00:00:00 ; 10 ;
"""
上面的ts=read_csv...
仍然可以正常工作,但是NÄ
打破了val
列并将其转换为字符串。但是当我将其更改为
ts = pd.read_csv(io.StringIO(ex2),'val'],na_values='NÄ')
使用na_values
,整个操作失败。 print(ts)
val
date
1939-10-01 1,1
1939-12-01 NÄ
1940-01-01 10
它不仅不接受NÄ
作为NaN
,还将所有val
转换为字符串,因此忽略了小数逗号并保留了尾随空格。 ts.val[0]
现在为' 1,1'
,因此简单的ts.val = ts.val.astype(float)
当然会失败。
na_values='NÄ'
在做什么?
为什么它还会破坏decimal','
并添加空格?
似乎skipinitialspace=True
应该有所帮助,但是当然NÄ
仍然会破坏val
列。
sep='\s*[;]s*'
看起来很有希望,并且
ts = pd.read_csv(io.StringIO(ex2),sep='\s*[;]s*',na_values='NÄ')
给出了一个貌似不错的print(ts)
val
date
1939-10-01 1.1
1939-12-01 NÄ
1940-01-01 10
(请注意小数点!),但是现在我有一个奇怪的情况,它确实替换了逗号,但是ts.val[0]
现在又是一个字符串,并且仍然带有尾随空格({{1} }。
那我该如何读取这些异常文件?
我当前使用的解决方法是使用纯python读取CSV文件(无论如何,我必须读取标题(实际文件中为40行)),然后将其写到适当的CSV文件中,以便使用熊猫:
' 1.1'
但是对于数千个CSV文件(最大大小为兆字节)来说,这并不是一个理想的解决方案,因此我想解决导入中的file = open(myfile,'r',encoding='UTF-8')
table = file.readlines()
file.close()
for v1 in range(0,len(table)):
table[v1] = table[v1].replace("NÄ","NaN")
table[v1] = table[v1].replace(",",".")
dataoutput = ["date;val\r\n"]
for v1 in range(3,len(table)):
dataoutput.append(table[v1])
f2 = open(myfile.replace('.csv','good.csv'),'w')
for v1 in range(0,len(dataoutput)):
f2.write(dataoutput[v1])
f2.close()
ts = pd.read_csv(myfile.replace('.csv',parse_dates=True)
ts.val = ts.val.astype(float)
问题。
解决方法
您的sep
指定不正确(以s*
而不是\s*
结尾,这意味着它正在寻找0到无限个s
个字符)。这就是为什么只捕获;
之后的前导空格而不是尾随空格的原因。顺便说一句,这也干扰了(1),因为您试图替换'NÄ'
,但值为' NÄ'
。请改用sep='\s*\;\s*'
。
您将来可以做的一件事是自己打印出有问题的值,以确保它们包含他们认为您包含的内容,例如ts.iloc[1].val
。
此外,如果使用统一代码的NaN
值有问题,您可以在解析之前将其剥离:
csv = io.StringIO(ex2.replace(u'N\xc4','[MISSING]'))
ts = pd.read_csv(csv,skiprows=4,decimal=',',index_col=0,usecols=[0,1],dayfirst=True,parse_dates=True,names=['date','val'],na_values='[MISSING]',sep='\s*\;\s*')
...这会给...
val
date
1939-10-01 1.1
1939-12-01 NaN
1940-01-01 10.0