【问题标题】:Ignoring bad rows of data in pandas.read_csv() that break header= keyword忽略 pandas.read_csv() 中破坏 header= 关键字的坏数据行
【发布时间】:2018-01-22 14:34:39
【问题描述】:

我有一系列非常混乱的 *.csv 文件正在被 pandas 读取。一个示例 csv 是:

Instrument 35392
"Log File Name : station"
"Setup Date (MMDDYY) : 031114"
"Setup Time (HHMMSS) : 073648"
"Starting Date (MMDDYY) : 031114"
"Starting Time (HHMMSS) : 090000"
"Stopping Date (MMDDYY) : 031115"
"Stopping Time (HHMMSS) : 235959"
"Interval (HHMMSS) : 010000"
"Sensor warmup (HHMMSS) : 000200"
"Circltr warmup (HHMMSS) : 000200" 


"Date","Time","","Temp","","SpCond","","Sal","","IBatt",""
"MMDDYY","HHMMSS","","øC","","mS/cm","","ppt","","Volts",""

"Random message here 031114 073721 to 031114 083200"
03/11/14,09:00:00,"",15.85,"",1.408,"",.74,"",6.2,""
03/11/14,10:00:00,"",15.99,"",1.96,"",1.05,"",6.3,""
03/11/14,11:00:00,"",14.2,"",40.8,"",26.12,"",6.2,""
03/11/14,12:00:01,"",14.2,"",41.7,"",26.77,"",6.2,""
03/11/14,13:00:00,"",14.5,"",41.3,"",26.52,"",6.2,""
03/11/14,14:00:00,"",14.96,"",41,"",26.29,"",6.2,""
"message 3"
"message 4"**

我一直在使用此代码导入 *csv 文件,处理双标题,拉出空列,然后删除包含错误数据的违规行:

DF = pd.read_csv(BADFILE,parse_dates={'Datetime_(ascii)': [0,1]}, sep=",", \
             header=[10,11],na_values=['','na', 'nan nan'], \
             skiprows=[10], encoding='cp1252')

DF = DF.dropna(how="all", axis=1)
DF = DF.dropna(thresh=2)
droplist = ['message', 'Random']
DF = DF[~DF['Datetime_(ascii)'].str.contains('|'.join(droplist))]

DF.head()

Datetime_(ascii)    (Temp, øC)  (SpCond, mS/cm) (Sal, ppt)  (IBatt, Volts)
0   03/11/14 09:00:00   15.85   1.408   0.74    6.2
1   03/11/14 10:00:00   15.99   1.960   1.05    6.3
2   03/11/14 11:00:00   14.20   40.800  26.12   6.2
3   03/11/14 12:00:01   14.20   41.700  26.77   6.2
4   03/11/14 13:00:00   14.50   41.300  26.52   6.2

在我有一个文件在标题后有一个错误的 1 行行之前,它工作得很好而且很花哨:“这里的随机消息 031114 073721 到 031114 083200”

我收到的错误是:

    *C:\Users\USER\AppData\Local\Continuum\Anaconda3\lib\site-
    packages\pandas\io\parsers.py in _do_date_conversions(self, names, data)
   1554             data, names = _process_date_conversion(
   1555                 data, self._date_conv, self.parse_dates, self.index_col,
    -> 1556                 self.index_names, names, 
    keep_date_col=self.keep_date_col)
   1557 
   1558         return names, data
    C:\Users\USER\AppData\Local\Continuum\Anaconda3\lib\site-
    packages\pandas\io\parsers.py in _process_date_conversion(data_dict, 
    converter, parse_spec, index_col, index_names, columns, keep_date_col)
   2975     if not keep_date_col:
   2976         for c in list(date_cols):
    -> 2977             data_dict.pop(c)
   2978             new_cols.remove(c)
   2979 
   KeyError: ('Time', 'HHMMSS')*

如果我删除该行,代码就可以正常工作。同样,如果我删除 header= 行,则代码可以正常工作。但是,我希望能够保留它,因为我正在阅读数百个这样的文件。

困难:我宁愿在调用 pandas.read_csv() 之前不要打开每个文件,因为这些文件可能相当大 - 因此我不想多次读取和保存!另外,我更喜欢真正的 pandas/pythonic 解决方案,它不涉及首先将文件作为 stringIO 缓冲区打开以删除有问题的行。

【问题讨论】:

  • 你能把错误的行贴出来吗?是不是每次出现错误时都会出现同一种错误行,或者某些文件的其他行可能存在其他类型的问题?
  • 产生错误的错误行是:“Random message here 031114 073721 to 031114 083200” 此行可能存在,也可能不存在于所有文件中。因此,我不能只增加 skiprows= 索引。此外,如果我更改该行的实际文本,错误仍然存​​在 - 文本是什么并不重要,但它是标题后只有 1 列的行。

标签: python pandas csv


【解决方案1】:

这是一种方法,利用skip_rows 接受可调用函数这一事实。该函数仅接收正在考虑的行索引,这是该参数的内置限制。

因此,可调用函数skip_test() 首先检查当前索引是否在要跳过的已知索引集中。如果不是,则打开实际文件并检查相应的行以查看其内容是否匹配。

skip_test() 函数在检查实际文件的意义上有点 hacky,尽管它只检查直到它正在评估的当前行索引。它还假设坏行总是以相同的字符串开头(在示例情况下,"foo"),但考虑到 OP,这似乎是一个安全的假设。

# example data
""" foo.csv
uid,a,b,c
0,1,2,3
skip me
1,11,22,33
foo
2,111,222,333 
"""

import pandas as pd

def skip_test(r, fn, fail_on, known):
    if r in known: # we know we always want to skip these
        return True
    # check if row index matches problem line in file
    # for efficiency, quit after we pass row index in file
    f = open(fn, "r")
    data = f.read()
    for i, line in enumerate(data.splitlines()):
        if (i == r) & line.startswith(fail_on):
            return True
        elif i > r:
            break
    return False

fname = "foo.csv"
fail_str = "foo"
known_skip = [2]
pd.read_csv(fname, sep=",", header=0, 
            skiprows=lambda x: skip_test(x, fname, fail_str, known_skip))
# output
   uid    a    b    c
0    0    1    2    3
1    1   11   22   33
2    2  111  222  333

如果您确切知道随机消息出现时将出现在哪一行,那么这会快得多,因为您可以告诉它不要检查文件内容以查找超出潜在违规行的任何索引。

【讨论】:

  • 谢谢!是的,我知道通过我的文件会出现什么消息,所以我可以解析它们。
【解决方案2】:

经过昨天的一些修补,我找到了解决方案以及潜在的问题可能是什么。

我尝试了上面的 skip_test() 函数答案,但我仍然收到表格大小错误:

pandas\_libs\parsers.pyx in pandas._libs.parsers.TextReader.read (pandas\_libs\parsers.c:10862)()

pandas\_libs\parsers.pyx in pandas._libs.parsers.TextReader._read_low_memory (pandas\_libs\parsers.c:11138)()

pandas\_libs\parsers.pyx in pandas._libs.parsers.TextReader._read_rows (pandas\_libs\parsers.c:11884)()

pandas\_libs\parsers.pyx in pandas._libs.parsers.TextReader._tokenize_rows (pandas\_libs\parsers.c:11755)()

pandas\_libs\parsers.pyx in pandas._libs.parsers.raise_parser_error (pandas\_libs\parsers.c:28765)()

ParserError: Error tokenizing data. C error: Expected 1 fields in line 14, saw 11

所以在玩弄了 skiprows= 之后,我发现我在使用 engine='c' 时并没有得到我想要的行为。 read_csv() 仍在根据前几行确定文件的大小,其中一些单列行仍在传递。可能是我的 csv 集中还有一些我没有计划的糟糕的单列行。

相反,我创建了一个任意大小的 DataFrame 作为模板。我拉入整个 .csv 文件,然后使用逻辑去除 NaN 行。

例如,我知道我的数据将遇到的最大表将是 10 行长。所以我对熊猫的呼吁是:

DF = pd.read_csv(csv_file, sep=',', \
     parse_dates={'Datetime_(ascii)': [0,1]},\
     na_values=['','na', '999999', '#'], engine='c',\ 
     encoding='cp1252', names = list(range(0,10)))

然后我使用这两行从 DataFrame 中删除 NaN 行和列:

#drop the null columns created by double deliminators
DF = DF.dropna(how="all", axis=1)
DF = DF.dropna(thresh=2)  # drop if we don't have at least 2 cells with real values

【讨论】:

    猜你喜欢
    • 2020-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多