【问题标题】:reading borked CSV files with pd.read_csv (irregular delimiters, custom NA)使用 pd.read_csv 读取 CSV 文件(不规则分隔符,自定义 NA)
【发布时间】:2020-12-17 07:47:19
【问题描述】:

我正在读取 CSV 数据以用作 pandas 数据框,但 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(简单)和一堆元数据(skiprows,也简单)。

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... 仍然可以正常工作,但 打破了val 列并将其转换为字符串。但是当我把它改成

ts = pd.read_csv(io.StringIO(ex2), skiprows=4, decimal=',', sep=';',
                 index_col=0, usecols=[0,1], dayfirst=True,
                 parse_dates=True, names=['date', 'val'],
                 na_values='NÄ')

使用na_values,整个事情都失败了。 print(ts)

                val
date
1939-10-01      1,1
1939-12-01     NÄ
1940-01-01    10

它不仅不接受NaN,这还将所有vals 转换为字符串,从而忽略小数逗号并保留尾随空格。 ts.val[0] 现在是 ' 1,1',所以一个简单的 ts.val = ts.val.astype(float) 当然会失败。

na_values='NÄ' 我做错了什么?

为什么还会破坏decimal','并添加空格?

似乎skipinitialspace=True 应该有所帮助,但当然 仍然会破坏val 列。 sep='\s*[;]s*' 看起来很有希望,而且

ts = pd.read_csv(io.StringIO(ex2), skiprows=4, decimal=',' ,sep='\s*[;]s*',
                 index_col=0, usecols=[0,1], dayfirst=True,
                 parse_dates=True, names=['date', 'val'],
                 na_values='NÄ')

给出了一个明显不错的print(ts)

               val
date
1939-10-01     1.1
1939-12-01      NÄ
1940-01-01      10

(注意小数点!),但现在我遇到了一个奇怪的情况,它确实替换了逗号,但ts.val[0] 现在又是一个字符串,并且仍然有尾随空格(' 1.1')。

那么我该如何阅读那些无聊的文件呢?

我目前使用的解决方法是使用纯 python 读取 CSV(无论如何我必须读取标题(实际文件中的 40 行))并将其写入正确的 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', 'good.csv'), 
                 sep=';', usecols=[0, 1], index_col=0,
                 dayfirst=True, parse_dates=True)

ts.val = ts.val.astype(float)

但是对于几千个 CSV 文件,大小达到一兆字节,这并不是真正的最佳解决方案,所以我想解决导入中的 问题。

【问题讨论】:

  • 我可能遗漏了一些东西,但可以在对 read_csv() 的调用中指定 dtypes,做到这一点,或者在文件解析后转换值?
  • 您可以将值指定为列表:na_values=[ '-1.#IND ', '1.#INF ' ];还要检查尾随空格(查看我的示例)。
  • dtype={'val':np.float64} 只是ValueError: Unable to convert column val to type <class 'numpy.float64'> 的错误并在列表中设置 (na_values=['NÄ']) 也不会改变结果。尾随(或前导)空格可能是一个问题。必须看看正则表达式的作用。
  • 设置为空格 (na_values='\s*[NÄ]\s*') 似乎没有任何改变。
  • 其他想法:检查编码。我对德语文件也有类似的问题。这是我完整的阅读行:df = pd.read_csv( join( path, filename ), sep=';', encoding='iso-8859-1', engine='c', skiprows=[0], usecols=colstouse, na_values=[ '-1.#IND ', ' 1.#INF ' ] )

标签: python pandas csv unicode separator


【解决方案1】:

您的sep 指定错误(它以s* 而不是\s* 结尾,这意味着它正在寻找介于0 和无限s 之间的字符)。这就是为什么您只捕获; 之后的前导而不是尾随空格的原因。顺便说一句,这也干扰了 (1),因为您试图替换 'NÄ',但值是 ' NÄ'。请改用sep='\s*\;\s*'

您将来可以做的一件事是自己打印出违规值,以确保它们包含他们认为您包含的内容,例如ts.iloc[1].val.

另外,如果 NaN 值在 unicode 中是一个问题,您可以在解析之前将其剥离:

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

【讨论】:

  • 当我不使用StringIO 而是使用pd.read_csv('file.csv, skiprows=... 进行示例时,uex.replace 如何工作?
  • csv = open('file.csv').replace(u'N\xc4', '[MISSING]') 中是否还缺少某些内容?我的结果是AttributeError: '_io.TextIOWrapper' object has no attribute 'replace'
  • "改用sep='\s*\;\s*'"该死的,我可以发誓我总是在s之前有\ 。当然,搜索字母 s 是没有意义的。修复sep 有效,不需要read().replace()(顺便说一句,我的机器上没有替换,我想文件编码会造成严重破坏)。那些 CSV 文件简直就是地狱。写它们的人讨厌所有不是记事本的东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-02-18
  • 2021-12-15
  • 1970-01-01
  • 2017-01-20
  • 2022-10-14
  • 2018-03-03
  • 1970-01-01
相关资源
最近更新 更多