Pandas读取带有空格的多级索引CSV文件

6

我将csv文件中的多行带空白的表头正确加载是有困难的。CSV文件如下:

,,C,,,D,,
A,B,X,Y,Z,X,Y,Z
1,2,3,4,5,6,7,8

我想要的结果是:
当我尝试使用 pd.read_csv(file, header=[0,1], sep=',') 加载时,我得到了以下结果:
是否有方法可以获得想要的结果?
注意:或者,我会接受这个结果作为一个结果。
版本信息:
Python: 2.7.8
Pandas 0.16.0
6个回答

8

这里有一种自动化的方式来修复列索引。首先,将列级别的值提取到DataFrame中:

columns = pd.DataFrame(df.columns.tolist())

然后将Unnamed:列重命名为NaN

columns.loc[columns[0].str.startswith('Unnamed:'), 0] = np.nan

然后向前填充 NaN 值:

columns[0] = columns[0].fillna(method='ffill')

因此,columns 现在看起来像

In [314]: columns
Out[314]: 
     0  1
0  NaN  A
1  NaN  B
2    C  X
3    C  Y
4    C  Z
5    D  X
6    D  Y
7    D  Z

现在我们可以寻找剩余的NaN值,并用空字符串填充它们:
mask = pd.isnull(columns[0])
columns[0] = columns[0].fillna('')

为了使第一列和第二列,即AB,可以像单层索引一样被索引为df['A']df['B'],您可以交换第一列和第二列中的值:
columns.loc[mask, [0,1]] = columns.loc[mask, [1,0]].values

现在你可以构建一个新的MultiIndex并将其分配给df.columns:
df.columns = pd.MultiIndex.from_tuples(columns.to_records(index=False).tolist())

把所有的东西结合在一起,如果 data 是...
,,C,,,D,,
A,B,X,Y,Z,X,Y,Z
1,2,3,4,5,6,7,8
3,4,5,6,7,8,9,0

那么。
import numpy as np
import pandas as pd
df = pd.read_csv('data', header=[0,1], sep=',')
columns = pd.DataFrame(df.columns.tolist())
columns.loc[columns[0].str.startswith('Unnamed:'), 0] = np.nan
columns[0] = columns[0].fillna(method='ffill')
mask = pd.isnull(columns[0])
columns[0] = columns[0].fillna('')
columns.loc[mask, [0,1]] = columns.loc[mask, [1,0]].values
df.columns = pd.MultiIndex.from_tuples(columns.to_records(index=False).tolist())
    print(df)

产量
   A  B  C        D      
         X  Y  Z  X  Y  Z
0  1  2  3  4  5  6  7  8
1  3  4  5  6  7  8  9  0

2

并没有什么神奇的方法可以让Pandas知道你希望索引是什么样子,最接近的方式就是自己指定很多东西,像这样:

names = ['A', 'B', 
         ('C','X'), ('C', 'Y'), ('C', 'Z'),
         ('D','X'), ('D','Y'), ('D', 'Z')]
pd.read_csv(file, mangle_dupe_cols=True,
            header=1, names=names, index_col=[0, 1])

提供:

     C        D      
     X  Y  Z  X  Y  Z
A B                  
1 2  3  4  5  6  7  8

为了以动态方式实现这一点,您可以按原样读取CSV的前两行,并循环遍历您获得的列以动态生成名称变量,然后再加载完整的数据集。
pd.read_csv(file, nrows=1, header=[0,1], index_col=[0, 1])

然后访问列并循环创建标题。 这不是一个非常干净的解决方案,但应该可以使用。

问题在于csv文件:1)有大量列;2)由另一个程序自动生成,因此从一次到另一次,列会发生变化。我猜应该有一种方法编写一个函数来循环处理第一层,如果为空,则重命名为左侧的名称或其他操作? - Julien Marrec

0

导入您的 CSV 文件并提供标题行索引:

df = pd.read_csv('file.csv', header=[0, 1, 2])

然后,您可以迭代每个列标题,对其进行清理,将其分配给元组,然后使用 pd.MultiIndex.from_tuples(list_of_tuples)重新分配数据框的列

df.columns = pd.MultiIndex.from_tuples(
[tuple(['' if y.find('Unnamed')==0 else y for y in x]) for x in df.columns]
)

这是我在尝试弄清楚这个问题时正在寻找的快速一行解决方法。

0

你可以使用以下方式进行阅读:

    df = pd.read_csv('file.csv', header=[0, 1], skipinitialspace=True, tupleize_cols=True)

然后

    df.columns = pd.MultiIndex.from_tuples(df.columns)

我得到的结果与使用 pd.read_csv(file, header=[0,1]) 得到的完全相同。 - Julien Marrec
你能粘贴一下你的CSV文件样本吗? - sachin saxena
@unutbu 已经很好心地在原问题中添加了 csv 代码(感谢!) - Julien Marrec

0

加载带有多级索引的数据框:

df = pd.read_csv(filelist,header=[0,1], sep=',')

编写一个函数来替换索引:
def replace_index(df):
    arr = df.columns.values
    l = [list(x) for x in arr]
    for i in range(len(l)):
        if l[i][0][:7] == 'Unnamed':
            if l[i-1][0][:7] != 'Unnamed':
                l[i][0] = l[i-1][0]
    for i in range(len(l)):
        if l[i][0][:7] == 'Unnamed':
                l[i][0] = l[i][1]
                l[i][1] = ''
    index = pd.MultiIndex.from_tuples(l)
    df.columns = index
    return df

返回正确索引的新数据框:

replace_index(df)

0

我使用了一种技术,将多级索引列展平为一个列。这对我来说效果很好。

your_df.columns = ['_'.join(col).strip() for col in your_df.columns.values]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接