Pandas:如何解决“错误标记数据”?

Posted

技术标签:

【中文标题】Pandas:如何解决“错误标记数据”?【英文标题】:Pandas: How to workaround "error tokenizing data"? 【发布时间】:2019-08-06 21:13:02 【问题描述】:

about this topic on SO 已经提出了很多问题。 (以及许多其他人)。 在众多答案中,到目前为止,没有一个对我真正有帮助。如果我错过了有用的,请告诉我。

我只是想将带有熊猫的 CSV 文件读入数据框。听起来很简单。

我的档案Test.csv

1,2,3,4,5
1,2,3,4,5,6
,,3,4,5
1,2,3,4,5,6,7
,2,,4

我的代码:

import pandas as pd
df = pd.read_csv('Test.csv',header=None)

我的错误:

pandas.errors.ParserError: Error tokenizing data. C error: Expected 5 fields in line 2, saw 6

我对这个问题的猜测是,Pandas 会查看第一行,并期望在接下来的行中有相同数量的标记。如果不是这种情况,它将因错误而停止。

在众多答案中,使用选项的建议是,例如: error_bad_lines=Falseheader=Noneskiprows=3 以及更多无用的建议。

但是,我不想忽略任何行或跳过。而且我事先不知道数据文件有多少列和行。

所以它基本上归结为如何找到数据文件中的最大列数。这是要走的路吗?我希望有一种简单的方法可以简单地读取第一行中没有最大列号的 CSV 文件。谢谢你的任何提示。我在 Win7 上使用 Python 3.6.3、Pandas 0.24.1。

【问题讨论】:

似乎与昨天有人遇到的相同问题:***.com/questions/55129640/…。要么读取整行并在之后拆分,要么更改原始文件添加包含过多列的标题行以确保安全,要么使用csv 模块 感谢您的链接。我会查的。我不想更改数据文件,我只是想阅读它。 是的,该 SO 链接中的赞成答案应该有助于解决这个问题。我喜欢pd.read_fwf('path_to_csv', header=None) 的解决方案,因为它只需要 csv 路径。 fwf 解决方案非常具体,并且很容易中断,因为基础数据实际上不是固定宽度分隔的。 似乎缺少对 pandas csv 的支持的原因是输入文件不是 csv。看起来像一个,但 csv 记录预计每条记录具有相同的字段序列。上面那个没有。因此,不要羞于对输入数据进行预处理以获得 csv。 en.wikipedia.org/wiki/Comma-separated_values#Specification 【参考方案1】:

感谢@ALollz 提供“非常新鲜”的链接(幸运的巧合)和@Rich Andrews 指出我的示例实际上不是“严格正确”的 CSV 数据。

所以,它暂时适用于我的方式改编自 @ALollz 的紧凑型解决方案 (https://***.com/a/55129746/7295599)

### reading an "incorrect" CSV to dataframe having a variable number of columns/tokens 
import pandas as pd

df = pd.read_csv('Test.csv', header=None, sep='\n')
df = df[0].str.split(',', expand=True)
# ... do some modifications with df
### end of code

df 包含空字符串 '' 用于开头和中间的缺失条目,None 用于结尾缺失的标记。

   0  1  2  3     4     5     6
0  1  2  3  4     5  None  None
1  1  2  3  4     5     6  None
2        3  4     5  None  None
3  1  2  3  4     5     6     7
4     2     4  None  None  None

如果您通过以下方式再次将其写入文件:

df.to_csv("Test.tab",sep="\t",header=False,index=False)

1   2   3   4   5       
1   2   3   4   5   6   
        3   4   5       
1   2   3   4   5   6   7
    2       4           

None 将转换为空字符串'',一切正常。

下一个级别是考虑引号中包含分隔符的数据字符串,但这是另一个主题。

1,2,3,4,5
,,3,"Hello, World!",5,6
1,2,3,4,5,6,7

【讨论】:

实际上你将如何解释包含分隔符的数据字符串? 我不能马上判断,我没有测试过。你?但可能有一种“智能”split() 命令的方法,也许是正则表达式...... 我在另一篇文章中找到了一种方法,在那里我使用 csv.reader 读取了 csv,它有一些参数。我使用了其中的 2 个:quotechar = '"' (它是单引号内的双引号,或者如果需要,您可以使用其他方式)和 skipinitialspace = True。我不能 100% 确定我的 csv 中是否需要跳过初始空间买她,它的工作原理......【参考方案2】:

使用宽容的 python csv 模块读取 csv,并在将加载的文件交给 pandas 之前修复它,这将在其他格式错误的 csv 数据上失败,无论 pandas 使用什么 csv 引擎。

import pandas as pd
import csv

not_csv = """1,2,3,4,5
1,2,3,4,5,6
,,3,4,5
1,2,3,4,5,6,7
,2,,4
"""

with open('not_a.csv', 'w') as csvfile:
    csvfile.write(not_csv)

d = []
with open('not_a.csv') as csvfile:
    areader = csv.reader(csvfile)
    max_elems = 0
    for row in areader:
        if max_elems < len(row): max_elems = len(row)
    csvfile.seek(0)
    for i, row in enumerate(areader):
        # fix my csv by padding the rows
        d.append(row + ["" for x in range(max_elems-len(row))])

df = pd.DataFrame(d)
print df

# the default engine
# provides "pandas.errors.ParserError: Error tokenizing data. C error: Expected 5 fields in line 2, saw 6 "
#df = pd.read_csv('Test.csv',header=None, engine='c')

# the python csv engine
# provides "pandas.errors.ParserError: Expected 6 fields in line 4, saw 7 "
#df = pd.read_csv('Test.csv',header=None, engine='python')

如果担心 python 内部的额外代码会创建太多 python 代码,请在 python 外部预处理文件。

Richs-MBP:tmp randrews$ cat test.csv
1,2,3
1,
2
1,2,
,,,
Richs-MBP:tmp randrews$ awk 'BEGIN FS=","; print $1","$2","$3","$4","$5' < test.csv
1,2,3,,
1,,,,
2,,,,
1,2,,,
,,,,

【讨论】:

感谢您的建议并指出我的示例实际上不是“严格正确”的 CSV 数据。我更喜欢保持简短,除了使用 Pandas 之外,不使用其他可能会进一步破坏我的最终可执行文件的模块。 如果担心代码量大、关注点分离、让其他人进行预处理等,也可以考虑在 python 之外处理文件 - 'awk' 是一个很好的工具,可以满足您的需求。我会将其附加到我的答案中。 感谢您的补充。除非绝对必要,否则我更喜欢使用尽可能少的工具、模块、外部程序、预处理步骤等。我没有使用过 awk,但听说过几次。【参考方案3】:

我对解决方案有不同的看法。让 pandas 负责创建表和删除 None 值,让我们负责编写适当的分词器。

分词器

def tokenize(str):
    idx = [x for x, v in enumerate(str) if v == '\"']
    if len(idx) % 2 != 0:
        idx = idx[:-1]
    memory = 
    for i in range(0, len(idx), 2):
        val = str[idx[i]:idx[i+1]+1]
        key = "_"*(len(val)-1)+"0".format(i)
        memory[key] = val
        str = str.replace(memory[key], key, 1)        
    return [memory.get(token, token) for token in str.split(",")]  

Tokenizer 测试用例

print (tokenize("1,2,3,4,5"))
print (tokenize(",,3,\"Hello, World!\",5,6"))
print (tokenize(",,3,\"Hello,,,, World!\",5,6"))
print (tokenize(",,3,\"Hello, World!\",5,6,,3,\"Hello, World!\",5,6"))
print (tokenize(",,3,\"Hello, World!\",5,6,,3,\"Hello,,5,6"))

输出

['1', '2', '3', '4', '5'] ['', '', '3', '"Hello, World!"', '5', '6'] ['', '', '3', '"Hello,,,, World!"', '5', '6'] ['', '', '3', '"Hello, World!"', '5', '6', '', '3', '"Hello, World!"', '5', '6'] ['', '', '3', '"Hello, World!"', '5', '6', '', '3', '"Hello', '', '5', '6']

将分词器付诸实施

with open("test1.csv", "r") as fp:
    lines = fp.readlines()

lines = list(map(lambda x: tokenize(x.strip()), lines))
df = pd.DataFrame(lines).replace(np.nan, '')

优势:

现在我们可以根据自己的需要使用分词器功能

【讨论】:

感谢您的建议。当绝对需要在引号中包含逗号时,我会(重新)考虑它。【参考方案4】:

就我而言 1 我在 Excel 中打开 *.csv 2 我将 *.csv 保存为 CSV(逗号分隔) 3 我通过以下方式在 python 中加载了文件:

import pandas as pd
df = pd.read_csv('yourcsvfile.csv', sep=',')

希望对你有帮助!

【讨论】:

好的,可能有用。但是抱歉,我绝对不想额外涉及 Excel!【参考方案5】:

对我来说,这是通过将 usecols 添加到 pd.read_csv() 命令来解决的:

usecols=['My_Column_1','My_Column_2',...]

【讨论】:

以上是关于Pandas:如何解决“错误标记数据”?的主要内容,如果未能解决你的问题,请参考以下文章

如何将带有文本信息的 1.3 GB csv 文件读入 Python 的 pandas 对象?

如何解决 pandas 的内存分配问题?

如何解决 jupyter notebook 中的 pandas 问题?

如何解决“模块‘pandas’没有属性‘scatter_matrix’”错误?

如何在 python Pandas 中执行/解决条件连接?

如何解决 Pandas 代码中的日期时间错误?