【问题标题】:Pandas read_csv add header names in case of changing number of columnsPandas read_csv 在更改列数的情况下添加标题名称
【发布时间】:2018-12-04 07:34:56
【问题描述】:

我有很多 csv 文件,我想用 Pandas (pd.read_csv) 读取,但是,在某些文件中,在中间添加了一个没有标题的列,比如这个例子:

Apples, Pears
1, 2
3, 4
5, 6, 7

如果使用 pd.read_csv(example_file) 会抛出以下错误“ParserError: Error tokenizing data. C error: Expected 2 fields in line 4, saw 3

我想避免跳过该行,而是添加一个虚拟标题名称,如 Unknown1,并获得以下结果:

Apples, Pears, Unknown1  
1, 2, np.nan
3, 4, np.nan
5, 6, 7

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    pandas 需要提前知道几何结构来构建数据框。您可以读取标题行并添加几个虚拟列名以提供列数,然后重新读取整个 csv 并丢弃根本未使用的列。

    >>> import pandas as pd
    >>> names = list(pd.read_csv('foo.csv', nrows=0)) + ['unknown1', 'unknown2']
    >>> df=pd.read_csv('foo.csv', names=names, skiprows=1).dropna(axis='columns', how='all')
    >>> df
       Apples   Pears  unknown1
    0       1       2       NaN
    1       3       4       NaN
    2       5       6       7.0
    

    如果有很多额外的列并且您担心中间数据帧的内存占用,您可以使用csv 模块扫描文件并计算最大行数。与pandas 不同,csv 非常乐意发出不同大小的行。

    >>> with open('foo.csv', newline='') as in_fp:
    ...     reader = csv.reader(in_fp)
    ...     header = next(reader)
    ...     num_cols = max(len(row) for row in reader)
    ... 
    >>> names = header + ['unknown{}'.format(i+1) for i in range(num_cols-len(header))]
    >>> df = pd.read_csv('foo.csv', names=names, skiprows=1)
    >>> df
       Apples   Pears  unknown1
    0       1       2       NaN
    1       3       4       NaN
    2       5       6       7.0
    

    【讨论】:

    • 太棒了!又好又简单,而且只使用 Pandas。谢谢! :)
    • 恐怕这是不可扩展的。如果缺少 10 个列名怎么办?
    • @sudonym - 这真的取决于你需要什么级别的扩展。预处理文件会增加 CPU 成本。除非文件很大,否则内存占用在现代硬件上并不是什么大问题。 OP 有很多文件和一些额外的列。这可能是一个很好的解决方案。
    • 我同意你的看法。
    【解决方案2】:

    我们可以加载 csv 然后修复你的问题

    import io
    t="""Apples, Pears
    1, 2
    3, 4
    5, 6, 7"""
    df = pd.read_csv(io.StringIO(t), sep='\t')
    
    yourdf=df.iloc[:,0].str.split(', ',expand=True)
    s=df.columns.str.split(', ').tolist()[0]
    yourdf.columns=s+['unknow'+str(x+1) for x in range(yourdf.shape[1]-len(s))]
    
    
    yourdf
    Out[104]: 
      Apples Pears unknow1
    0      1     2    None
    1      3     4    None
    2      5     6       7
    

    【讨论】:

    • 如果 't' 是 csv 文件的路径,如何使用 IO?
    【解决方案3】:

    如果您事先不知道列数,可以使用 readlines() 预先确定所有行的最大列数,但代价是丢失已知的标题名称。

    sep = ','                                                   # Define separator
    lines = open("test.csv").readlines()                        # Open file and read lines
    colcount = max([len(l.strip().split(sep)) for l in lines])  # Count separator
    df = pd.read_csv("test.csv", names = range(colcount), skiprows = [0])
    print df
    
       0  1    2
    0  1  2  NaN
    1  3  4  NaN
    2  5  6  7.0
    

    上面的colcount 也可以应用于到目前为止的所有其他答案。


    编辑:注意 .csv 以外的输入文件(请参阅 cmets)

    【讨论】:

    • 这也不起作用。您忽略了 CSV 处理器会考虑的列分隔符的任何引用和转义。
    • 这对我有用 - 您是否复制并粘贴到 jupyter 中?您介意详细说明吗?
    • 引用和转义是 csv 协议的一部分。例如,如果列是"embedded comma, should not split this column" 怎么办? CSV 解析处理这个问题,这就是我们使用它们而不是仅仅拆分的原因。
    • 我赞成您的回答,并删除了我关于您的可扩展性的任何参考。谢谢你的解释。
    • 如果你使用csv来处理转义,那么这个解决方案很好。
    猜你喜欢
    • 2013-12-15
    • 2019-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-01
    • 1970-01-01
    相关资源
    最近更新 更多