由于额外的列值,尝试使用 pandas Python 读取 csv 时出错

Posted

技术标签:

【中文标题】由于额外的列值,尝试使用 pandas Python 读取 csv 时出错【英文标题】:Getting error while trying to read csv using pandas Python due to extra column values 【发布时间】:2019-10-06 18:59:06 【问题描述】:

这是我想要摆脱的场景: 我正在尝试阅读以下类型的 csv:

para1,para2,para3,para4
1,2,3,4,
1,2,3,4,5,
1,2,3,4,
2,3,4,5,6,7,8,9,0,

我正在使用以下命令并收到以下错误:

>>> import pandas as pd
>>> df =pd.read_csv("test.csv")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python35\lib\site-packages\pandas\io\parsers.py", line 702, in parser_f
    return _read(filepath_or_buffer, kwds)
  File "C:\Python35\lib\site-packages\pandas\io\parsers.py", line 435, in _read
    data = parser.read(nrows)
  File "C:\Python35\lib\site-packages\pandas\io\parsers.py", line 1139, in read
    ret = self._engine.read(nrows)
  File "C:\Python35\lib\site-packages\pandas\io\parsers.py", line 1995, in read
    data = self._reader.read(nrows)
  File "pandas\_libs\parsers.pyx", line 899, in pandas._libs.parsers.TextReader.read
  File "pandas\_libs\parsers.pyx", line 914, in pandas._libs.parsers.TextReader._read_low_memory
  File "pandas\_libs\parsers.pyx", line 968, in pandas._libs.parsers.TextReader._read_rows
  File "pandas\_libs\parsers.pyx", line 955, in pandas._libs.parsers.TextReader._tokenize_rows
  File "pandas\_libs\parsers.pyx", line 2172, in pandas._libs.parsers.raise_parser_error
pandas.errors.ParserError: Error tokenizing data. C error: Expected 4 fields in line 3, saw 5

我试图搜索问题并在 SO:Python Pandas Error tokenizing data 上找到了这个帖子

所以,我试过了。这不是我所期待的。它正在截断值。

>>> df =pd.read_csv("test.csv",error_bad_lines=False)
b'Skipping line 3: expected 4 fields, saw 5\nSkipping line 5: expected 4 fields, saw 9\n'
>>> df


para1  para2  para3  para4
0      1      2      3      4
1      1      2      3      4

我想要的是这样的: 如果有额外的值,则将列作为在额外中找到的最高列的整数值。然后将其余值设为零(0)直到最后一列并读取 csv。

我期待的输出是这样的:

>>> df =pd.read_csv("test.csv")
>>> df
   para1  para2  para3  para4    0    1    2    3    4
0      1      2      3      4  NaN  NaN  NaN  NaN  NaN
1      1      2      3      4  5.0  NaN  NaN  NaN  NaN
2      1      2      3      4  NaN  NaN  NaN  NaN  NaN
3      2      3      4      5  6.0  7.0  8.0  9.0  0.0
>>> df = df.fillna(0)
>>> df
   para1  para2  para3  para4    0    1    2    3    4
0      1      2      3      4  0.0  0.0  0.0  0.0  0.0
1      1      2      3      4  5.0  0.0  0.0  0.0  0.0
2      1      2      3      4  0.0  0.0  0.0  0.0  0.0
3      2      3      4      5  6.0  7.0  8.0  9.0  0.0

但是请注意,我不想照顾专栏。相反,程序必须自动理解并制作上面给出的列标题。

其次,请尽量避免建议我写标题。因为可能有很多列我可能无法编写标题,而只是保持原样。所以缺少的列标题将是如上所述的数字整数。有人有任何查询的解决方案,请告诉我?

【问题讨论】:

不是有效的解决方案,但可以工作..您可以先读取没有标题的 csv 文件,然后第二次您可以只读取标题 @AkshayNevrekar 可能是,但没有可用的熊猫解决方案吗? 以上只是pandas解决方案......也许你的意思是问是否可以通过调整read_csv中的一些参数来完成,至少我不知道。 @AkshayNevrekar 是的,类似 我已经编辑了示例,因为我忘了在那里添加额外的逗号 【参考方案1】:

我不确定是否有更简洁的方法来执行此操作,但我对其进行了测试,并且仅使用 pandas 即可:

df = pd.read_csv('test.csv', header=None, sep='\n')
df= df[0].str.split(',', expand=True)
new_header = df.iloc[0].fillna(df.columns.to_series())
df = df[1:]
df.columns = new_header

【讨论】:

假设 1,"2,2,2",3 是 5 列,而不是像真正的 CSV 解析器那样的 3 列 我同意,如果您的数据结构需要更好的解决方案,它将无法工作,但它适用于他发布的 OP 的问题数据。【参考方案2】:

好的,这意味着您必须解析文件直到其结束才能获得实际的列数,因为pandas.read_csv 没有针对该要求的规定。

如果高性能不是问题 (*),一个简单的方法是依靠良好的旧 csv 模块并根据需要动态添加列:

with open('test.csv') as fd:
    rd = csv.reader(fd)
    header = next(rd)     # initialize column names from first row
    next_key = 0          # additional columns will start at '0'
    data = k: list() for k in header  # initialize data list per column
    for row in rd:
        while len(row) > len(header):    # add eventual new columns
            header.append(str(next_key))
            data[header[-1]] = [np.nan] * len(data[header[0]])
            next_key += 1                # increase next column name
        # eventually extend the row up to the header size
        row.extend([np.nan] * (len(header) - len(row)))
        # and add data to the column lists
        for i, k in enumerate(header): data[k].append(row[i])

# data is now in a dict format, suitable to feed DataFrame
df = pd.DataFrame(data)

(*) 上面的代码效率不是很高,因为它一次将元素添加到列表中。这对于 pandas DataFrame 来说是很糟糕的,即使对于 Python 列表也不是很好。可以通过在 numpy.ndarray 中分配束来改进它,但代价是增加了复杂性。

【讨论】:

这可能会有所帮助。但是If performance is not a concern (*) 是什么意思?显然,性能是一个值得考虑的问题。 @JafferWilson:我的意思是这段代码并不像read_csv 的C 解析器那样高效。在相当新的计算机上,它应该能够处理高达几兆字节的 csv 文件。如果您打算处理许多几 GB 的文件,则应该进行低级优化。 @JafferWilson: ...换一种说法,我的建议是你应该试一试,只有当性能不可接受时,才尝试使用 pre_allocation 来加快速度。 我一定会试一试的【参考方案3】:

尝试使用下面的代码,使用sep=' ',然后使用iloc 获取第一列,然后简单地使用str.splitexpand=True 来创建一个新的数据框,然后使用fillna 替换NaNs ,然后最后一行是用list 理解和list(range(...)) 命名列。

所以你应该使用:

df = pd.read_csv("test.csv", sep='  ')
df2 = df.iloc[:, 0].str.replace(',$', '').str.split(',', expand=True).fillna(0)
dd = df.columns[0].split(',')
ff = [str(x) for x in range(len(df2.columns) - len(dd))]
df2.columns = dd + ff
print(df2)

【讨论】:

我如何确定是否添加list(range(5))list(range(10)) 或其他任何内容?如果我有一个大数据集,我什至无法打开它并计算要添加多少。你能帮我解决这个问题吗? @JafferWilson 再次编辑我的 @JafferWilson 很高兴再次提供帮助 :-)

以上是关于由于额外的列值,尝试使用 pandas Python 读取 csv 时出错的主要内容,如果未能解决你的问题,请参考以下文章

Laravel - 从数据透视表中获取额外的列值

删除pandas数据帧中的重复项后,替换特定的列值

Pandas - 获取行匹配条件的列值[重复]

python用额外的列连接替换数据框列值

有没有一种有效的方法来计算 Pandas 中的列值,使用基于其他列的条件值的前行的值?

将 pandas GroupBy 中的列值聚合为 dict