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

时间:2019-05-20 11:45:38

标签: python pandas

这是我要摆脱的情况:
我正在尝试阅读以下类型的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

我想要的是这样的东西:
如果有多余的值,则将这些列作为整数值,并在Extra中找到最高的列。然后将其余值设为零(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

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

第二,请尽量避免建议我写标题。由于可能有许多列,我可能无法写标题,而仅保留它。因此缺少的列标题将是如上所述的整数。有人对查询有任何解决方案吗,请让我知道?

3 个答案:

答案 0 :(得分:3)

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

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)

好吧,这意味着您必须解析文件直到其结束以获取实际的列数,因为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中分配束来改善它,但是代价是增加复杂性。

答案 2 :(得分:1)

尝试使用以下代码,先使用sep=' ',然后使用iloc,它会获得第一列,然后依次是str.splitexpand=True,这将创建一个新的数据帧,然后fillna替换NaN,然后最后一行是用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)