具有双层表头的pandas数据框并导出为csv

10 投票
4 回答
16344 浏览
提问于 2025-04-18 10:48

我有一个数据表

df = pd.DataFrame(columns = ["AA", "BB", "CC"])
df.loc[0]= ["a", "b", "c1"]
df.loc[1]= ["a", "b", "c2"]
df.loc[2]= ["a", "b", "c3"]

我需要在表头上添加第二行

df.columns = pd.MultiIndex.from_tuples(zip(df.columns, ["DD", "EE", "FF"]))

现在我的数据表是这样的

  AA BB  CC
  DD EE  FF
0  a  b  c1
1  a  b  c2
2  a  b  c3

但是当我把这个数据表写入CSV文件时

df.to_csv("test.csv", index = False)

我发现多了一行,不是我预期的那样

AA,BB,CC
DD,EE,FF
,,
a,b,c1
a,b,c2
a,b,c3

4 个回答

2

在@DSM的解决方案基础上:

如果你需要(就像我一样)把同样的技巧应用到导出到excel,主要需要的改变(除了与to_excel方法的预期差异)就是要去掉用于列标签的多重索引...

这是因为.to_excel不支持写出一个列有多重索引但没有索引的数据框(如果在.to_excel方法中设置index=False),而.to_csv是可以的。

总之,下面就是它的样子:

>>> writer = pd.ExcelWriter("noblankrows.xlsx")
>>> headers = pd.DataFrame(df.columns.tolist()).T
>>> headers.to_excel(
        writer, header=False, index=False)
>>> df.columns = pd.Index(range(len(df.columns)))  # that's what I was referring to...
>>> df.to_excel(
        writer, header=False, index=False, startrow=len(headers))
>>> writer.save()
>>> pd.read_excel("noblankrows.xlsx").to_csv(sys.stdout, index=False)
AA,BB,CC
DD,EE,FF
a,b,c1
a,b,c2
a,b,c3
3

使用 df.to_csv("test.csv", index = False, tupleize_cols=True) 这个命令,可以把结果保存成一个CSV文件,内容如下:

"('AA', 'DD')","('BB', 'EE')","('CC', 'FF')"
a,b,c1
a,b,c2
a,b,c3

如果你想把这个CSV文件再读回来,可以用:

df2=pd.read_csv("test.csv", tupleize_cols=True)
df2.columns=pd.MultiIndex.from_tuples(eval(','.join(df2.columns)))

如果你想得到你想要的确切输出,可以使用:

with open('test.csv', 'a') as f:
    pd.DataFrame(np.asanyarray(df.columns.tolist())).T.to_csv(f, index = False, header=False)
    df.to_csv(f, index = False, header=False)
4

我觉得这是to_csv里的一个bug。如果你想找解决办法,这里有几个方法。

要重新读取这个csv文件,可以指定表头行*:

In [11]: csv = "AA,BB,CC
DD,EE,FF
,,
a,b,c1
a,b,c2
a,b,c3"

In [12]: pd.read_csv(StringIO(csv), header=[0, 1])
Out[12]:
  AA BB  CC
  DD EE  FF
0  a  b  c1
1  a  b  c2
2  a  b  c3

*奇怪的是,这似乎会忽略空行。

如果要写入数据,可以先写表头,然后再追加数据:

with open('test.csv', 'w') as f:
    f.write('\n'.join([','.join(h) for h in zip(*df.columns)]) + '\n')
df.to_csv('test.csv', mode='a', index=False, header=False)

注意这里的to_csv部分是针对多重索引列的:

In [21]: '\n'.join([','.join(h) for h in zip(*df.columns)]) + '\n'
Out[21]: 'AA,BB,CC\nDD,EE,FF\n'
8

这虽然是个不太优雅的解决办法,但如果你现在就需要让某个东西能用,你可以把它分成两个部分来写:

>>> pd.DataFrame(df.columns.tolist()).T.to_csv("noblankrows.csv", mode="w", header=False, index=False)
>>> df.to_csv("noblankrows.csv", mode="a", header=False, index=False)
>>> !cat noblankrows.csv
AA,BB,CC
DD,EE,FF
a,b,c1
a,b,c2
a,b,c3

撰写回答