繁体   English   中英

Pandas dataframe read_csv on bad data

[英]Pandas dataframe read_csv on bad data

我想阅读一个非常大的 csv(无法在 excel 中打开并轻松编辑),但在第 100,000 行左右,有一行有一个额外的列导致程序崩溃。 这一行是错误的,所以我需要一种方法来忽略它是一个额外的列的事实。 大约有 50 列,因此对标题进行硬编码并使用名称或 usecols 是不可取的。 我也可能会在其他 csv 中遇到这个问题并想要一个通用的解决方案。 不幸的是,我在 read_csv 中找不到任何东西。 代码很简单:

def loadCSV(filePath):
    dataframe = pd.read_csv(filePath, index_col=False, encoding='iso-8859-1', nrows=1000)
    datakeys = dataframe.keys();
    return dataframe, datakeys

通过error_bad_lines=False跳过错误的行:

error_bad_lines : 布尔值,默认情况下具有太多字段的 True 行(例如,带有太多逗号的 csv 行)将默认导致引发异常,并且不会返回任何 DataFrame。 如果为 False,那么这些“坏行”将从返回的 DataFrame 中删除。 (仅对 C 解析器有效)

要获取有关导致错误的行的信息,请尝试使用error_bad_lines=Falsewarn_bad_lines=True

dataframe = pd.read_csv(filePath, index_col=False, encoding='iso-8859-1', nrows=1000,
                        warn_bad_lines=True, error_bad_lines=False)

error_bad_lines=False跳过引起错误的行, warn_bad_lines=True打印错误详细信息和行号,如下所示:

'Skipping line 3: expected 4 fields, saw 3401\nSkipping line 4: expected 4 fields, saw 30...'

如果您想保存警告消息(即用于进一步处理),那么您也可以将其保存到文件中(使用contextlib ):

import contextlib

with open(r'D:\Temp\log.txt', 'w') as log:
    with contextlib.redirect_stderr(log):
        dataframe = pd.read_csv(filePath, index_col=False, encoding='iso-8859-1', 
                                warn_bad_lines=True, error_bad_lines=False)

1.4.0 的新功能

pandas 1.4.0开始, read_csv()通过允许将可调用对象分配给on_bad_lines= ,提供允许您以更优雅和智能的方式处理这些情况的功能。

例如,假设CSV可能导致错误数据错误: Expected 4 fields in line 3, saw 5

C1,C2,C3,C4
10,11,12,13
25,26,27,28,garbage
80,81,82,83

这个 function 只是忽略了坏行中的最后一列(正如上面原始问题陈述中所期望的那样):

df = pd.read_csv('your.csv', on_bad_lines=lambda x: x[:-1], engine='python')
df

   C1  C2  C3  C4
0  10  11  12  13
1  25  26  27  28
2  80  81  82  83

on_bad_lines可调用 function 在每个坏行上调用,并具有 function 签名(bad_line: list[str]) -> list[str] | None (bad_line: list[str]) -> list[str] | None 如果 function 返回None ,坏行将被忽略。 如您所见, engine='python'是必需的。

这样做的好处在于,它为您想要编写任何细粒度的逻辑来解决问题打开了大门。

例如,假设您想从行的开头或结尾删除错误数据,如果开头和结尾都有错误数据,则只需忽略该行,您可以:

CSV

C1,C2,C3,C4
10,11,12,13
20,21,22,23,garbage
60,61,62,63
trash,80,81,82,83
trash,90,91,82,garbage

Function 定义

def line_fixer(x):
    if not x[0].isnumeric() and x[-1].isnumeric():
        return x[1:] 
    
    if not x[-1].isnumeric() and x[0].isnumeric():
        return x[:-1]
    
    return None

结果

df = pd.read_csv('your.csv', on_bad_lines=line_fixer, engine='python')
df

   C1  C2  C3  C4
0  10  11  12  13
1  20  21  22  23
2  60  61  62  63
3  80  81  82  83

对于像我这样在原版发布后几年遇到的人,其他答案建议使用error_bad_lines=Falsewarn_bad_lines=True ,但两者都在熊猫中被弃用。 而是使用on_bad_lines = 'warn'来实现相同的效果来跳过坏数据线。

on_bad_lines = 'warn'遇到坏行时会发出警告并跳过该行。

on_bad_lines其他可接受的值

  • 'error' 在坏行上引发异常
  • 'skip' 将跳过任何坏行

这是我解决这些问题的方法,它很慢但效果很好,简单地说只需将 CSV 文件读取为 txt 文件,然后遍历每一行。 如果“,”逗号小于它应该只是跳过该行。 最终安全的正确线路。

def bad_lines(path):
    import itertools
    num_columns = []
    lines = ""
    
    for i in range(10,50,5):
        content = open(path).readlines(i)[0]
        if (content.count("'") == 0) and (content.count('"') == 0):
            num_columns.append(content.count(","))

    if len(set(num_columns)) == 1:
        for line in itertools.islice(open(path), 0, None):
            if line.count(",") >= num_columns[0]:
                lines = lines + line

    text_file = open("temp.txt", "w")
    n = text_file.write(lines)
    text_file.close()
    
    return("temp.txt")

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM