【问题标题】:using pandas read_csv with missing data使用带有缺失数据的 pandas read_csv
【发布时间】:2016-10-04 01:42:24
【问题描述】:

我正在尝试读取一个 csv 文件,其中某些行可能缺少数据块。

当您指定 dtype 时,这似乎会导致 pandas read_csv 函数出现问题。问题似乎是为了从 str 转换为 dtype 指定的任何内容,pandas 只是尝试直接转换它。因此,如果缺少某些东西,事情就会崩溃。

随后是 MWE(此 MWE 使用 StringIO 代替真实文件;但是,使用真实文件时也会出现此问题)

import pandas as pd
import numpy as np
import io

datfile = io.StringIO("12 23 43| | 37| 12.23| 71.3\n12 23 55|X|   |      | 72.3")

names = ['id', 'flag', 'number', 'data', 'data2']
dtypes = [np.str, np.str, np.int, np.float, np.float]

dform = {name: dtypes[ind] for ind, name in enumerate(names)}

colconverters = {0: lambda s: s.strip(), 1: lambda s: s.strip()}

df = pd.read_table(datfile, sep='|', dtype=dform, converters=colconverters, header=None,
                   index_col=0, names=names, na_values=' ')

我运行时遇到的错误是

Traceback (most recent call last):
  File "pandas/parser.pyx", line 1084, in pandas.parser.TextReader._convert_tokens (pandas/parser.c:12580)
TypeError: Cannot cast array from dtype('O') to dtype('int64') according to the rule 'safe'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/aliounis/Repos/stellarpy/source/mwe.py", line 15, in <module>
    index_col=0, names=names, na_values=' ')
  File "/usr/local/lib/python3.5/site-packages/pandas/io/parsers.py", line 562, in parser_f
    return _read(filepath_or_buffer, kwds)
  File "/usr/local/lib/python3.5/site-packages/pandas/io/parsers.py", line 325, in _read
    return parser.read()
  File "/usr/local/lib/python3.5/site-packages/pandas/io/parsers.py", line 815, in read
    ret = self._engine.read(nrows)
  File "/usr/local/lib/python3.5/site-packages/pandas/io/parsers.py", line 1314, in read
    data = self._reader.read(nrows)
  File "pandas/parser.pyx", line 805, in pandas.parser.TextReader.read (pandas/parser.c:8748)
  File "pandas/parser.pyx", line 827, in pandas.parser.TextReader._read_low_memory (pandas/parser.c:9003)
  File "pandas/parser.pyx", line 904, in pandas.parser.TextReader._read_rows (pandas/parser.c:10022)
  File "pandas/parser.pyx", line 1011, in pandas.parser.TextReader._convert_column_data (pandas/parser.c:11397)
  File "pandas/parser.pyx", line 1090, in pandas.parser.TextReader._convert_tokens (pandas/parser.c:12656)
ValueError: invalid literal for int() with base 10: '   '

有什么办法可以解决这个问题。我查看了文档,但没有看到任何看起来可以直接解决此解决方案的内容。这只是一个需要向熊猫报告的错误吗?

【问题讨论】:

    标签: python csv numpy pandas


    【解决方案1】:

    试试这个:

    import pandas as pd
    import numpy as np
    import io
    
    datfile = io.StringIO(u"12 23 43| | 37| 12.23| 71.3\n12 23 55|X|   |      | 72.3")
    
    names  = ['id', 'flag', 'number', 'data', 'data2']
    dtypes = [np.str, np.str, np.str, np.float, np.float] 
    dform  = {name: dtypes[ind] for ind, name in enumerate(names)}
    
    colconverters = {0: lambda s: s.strip(), 1: lambda s: s.strip()}
    
    df     = pd.read_table(datfile, sep='|', dtype=dform, converters=colconverters, header=None, na_values=' ')
    df.columns = names
    

    编辑:转换 dtypes 后导入。

    df["number"] = df["data"].astype('int')
    df["data"]   = df["data"].astype('float')
    

    您的数据混合了 str 和 numbers 等空白。

    <class 'pandas.core.frame.DataFrame'>
    RangeIndex: 2 entries, 0 to 1
    Data columns (total 5 columns):
    id        2 non-null object
    flag      2 non-null object
    number    2 non-null object
    data      2 non-null object
    data2     2 non-null float64
    dtypes: float64(1), object(4)
    memory usage: 152.0+ bytes
    

    如果您查看data,它是np.float,但已转换为对象,而data2np.float,直到空白,然后它也会变成对象。

    【讨论】:

    • 虽然运行没有错误,但它实际上只是将所有内容作为字符串读取,因为names 字段用于查询dtypes 字典以查看要将值转换为什么。
    【解决方案2】:

    所以,正如 Merlin 所指出的,主要问题是 nan 不能是整数,这可能就是 pandas 一开始就采取这种方式的原因。不幸的是,我别无选择,所以我不得不自己对 pandas 源代码进行一些更改。我最终不得不将文件 parser.pyx 的第 1087-1096 行更改为

            na_count_old = na_count
            print(col_res)
            for ind, row in enumerate(col_res):
                k = kh_get_str(na_hashset, row.strip().encode())
                if k != na_hashset.n_buckets:
    
                    col_res[ind] = np.nan
    
                    na_count += 1
    
                else:
    
                    col_res[ind] = np.array(col_res[ind]).astype(col_dtype).item(0)
    
            if na_count_old==na_count:
    
                # float -> int conversions can fail the above
                # even with no nans
                col_res_orig = col_res
                col_res = col_res.astype(col_dtype)
                if (col_res != col_res_orig).any():
                    raise ValueError("cannot safely convert passed user dtype of "
                                     "{col_dtype} for {col_res} dtyped data in "
                                     "column {column}".format(col_dtype=col_dtype,
                                                              col_res=col_res_orig.dtype.name,
                                                              column=i))
    

    本质上遍历列的每个元素,检查每个元素是否包含在 na 列表中(请注意,我们必须剥离这些东西,以便多空格显示在 na 列表中)。如果是,则将该元素设置为双精度 np.nan。如果它不在 na 列表中,则将其转换为为该列指定的原始 dtype(这意味着该列将具有多个 dtype)。

    虽然这不是一个完美的解决方案(而且可能很慢),但它可以满足我的需求,也许遇到类似问题的其他人会发现它很有用。

    【讨论】:

    • 对于每个人来说,我从 0.06 开始就一直在使用 pandas,并且我没有更改源代码。我建议不要经常这样做。 Pandas 是一只不断进化的野兽,无论如何你都会发现自己在升级时修改代码。并且,然后跟踪您修改源代码以启动的所有位置。但是,我以前见过这样做。祝你好运。
    • @Merlin 在一个周末之后,我想我明白你的答案是什么了。只需将事物作为文本文件读入,然后转换为所需的 dtype。我同意你关于不更改源代码的观点(事实上这是我第一次修改别人的源代码)。与您提出的建议相比,我认为这样做的唯一好处是这是一个 cython 实现,因此循环应该更快。如果您想在阅读文本后修改您的答案以显示如何进行 dtype 转换,我会改为接受您的答案,因为这样更易于管理。
    猜你喜欢
    • 2017-02-10
    • 2021-04-18
    • 1970-01-01
    • 2020-02-28
    • 2013-04-08
    • 2019-07-17
    • 1970-01-01
    • 2018-07-26
    • 2019-09-02
    相关资源
    最近更新 更多