Pandas 在行上设置多索引,然后转置到列

Posted

技术标签:

【中文标题】Pandas 在行上设置多索引,然后转置到列【英文标题】:Pandas setting multi-index on rows, then transposing to columns 【发布时间】:2016-12-23 04:07:29 【问题描述】:

如果我有一个简单的数据框:

print(a)

  one  two three
0   A    1     a
1   A    2     b
2   B    1     c
3   B    2     d
4   C    1     e
5   C    2     f

我可以通过发出以下命令轻松地在行上创建多索引:

a.set_index(['one', 'two'])

        three
one two      
A   1       a
    2       b
B   1       c
    2       d
C   1       e
    2       f

是否有类似的简单方法可以在列上创建多索引?

我想结束:

    one A       B       C   
    two 1   2   1   2   1   2
    0   a   b   c   d   e   f

在这种情况下,创建行多索引然后转置它会非常简单,但在其他示例中,我希望在行和列上都创建多索引。

【问题讨论】:

看起来 a.pivot(index='one', columns='two', values='three') 越来越接近我想要的(从 df 中提取信息并将它们变成列),虽然我还没有完全弄清楚如何制作多索引。 我认为您不想“在列上设置多索引”,我认为您想在行上设置它,然后将行转置为列?请编辑您的问题以使其更清楚 【参考方案1】:

是的!这叫换位。

a.set_index(['one', 'two']).T


让我们借用 @ragesz 的帖子,因为他们使用了一个更好的例子来演示。

df = pd.DataFrame('a':['foo_0', 'bar_0', 1, 2, 3], 'b':['foo_0', 'bar_1', 11, 12, 13],
    'c':['foo_1', 'bar_0', 21, 22, 23], 'd':['foo_1', 'bar_1', 31, 32, 33])

df.T.set_index([0, 1]).T

【讨论】:

OP,不想使用转置,因为他们希望在列和行上有多重索引。 可能需要 .reset_index(drop=True)df.columns.name = ['first', 'second'] 来重命名列标题。 @Merlin 没关系。您可以转置然后 set_index,转置然后 set_index。如果 OP put 是他们都喜欢的一个例子,我很高兴展示它是如何完成的。我现在就做一个,但我得跑一会儿。 @piRSquared,我也是这么想的。【参考方案2】:

您可以使用pivot_table,然后对数据框进行一系列操作以获得所需的形式:

df_pivot = pd.pivot_table(df, index=['one', 'two'], values='three', aggfunc=np.sum)

def rename_duplicates(old_list):    # Replace duplicates in the index with an empty string
    seen = 
    for x in old_list:
        if x in seen:
            seen[x] += 1
            yield " " 
        else:
            seen[x] = 0
            yield x

col_group = df_pivot.unstack().stack().reset_index(level=-1)
col_group.index = rename_duplicates(col_group.index.tolist())
col_group.index.name = df_pivot.index.names[0]
col_group.T

one  A     B     C   
two  1  2  1  2  1  2
0    a  b  c  d  e  f

【讨论】:

【参考方案3】:

我认为简短的回答是 NO。要拥有多索引列,数据框应该有两行(或更多)要转换为标题的行(如多索引行的列)。如果您有这种数据框,创建多索引标头并不是那么困难。它可以在很长的代码行中完成,并且您可以在任何其他数据帧中重复使用它,只应记住标题的行号并在不同时更改:

df = pd.DataFrame('a':['foo_0', 'bar_0', 1, 2, 3], 'b':['foo_0', 'bar_1', 11, 12, 13],
    'c':['foo_1', 'bar_0', 21, 22, 23], 'd':['foo_1', 'bar_1', 31, 32, 33])

数据框:

       a      b      c      d
0  foo_0  foo_0  foo_1  foo_1
1  bar_0  bar_1  bar_0  bar_1
2      1     11     21     31
3      2     12     22     32
4      3     13     23     33

创建多索引对象:

arrays = [df.iloc[0].tolist(), df.iloc[1].tolist()]
tuples = list(zip(*arrays))
index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second'])

df.columns = index

多索引头结果:

first   foo_0         foo_1       
second  bar_0  bar_1  bar_0  bar_1
0       foo_0  foo_0  foo_1  foo_1
1       bar_0  bar_1  bar_0  bar_1
2           1     11     21     31
3           2     12     22     32
4           3     13     23     33

最后我们需要删除 0-1 行然后重置行索引:

df = df.iloc[2:].reset_index(drop=True)

“单行”版本(您唯一需要更改的是指定标头索引和数据框本身):

idx_first_header = 0
idx_second_header = 1

df.columns = pd.MultiIndex.from_tuples(list(zip(*[df.iloc[idx_first_header].tolist(),
    df.iloc[idx_second_header].tolist()])), names=['first', 'second'])

df = df.drop([idx_first_header, idx_second_header], axis=0).reset_index(drop=True)

【讨论】:

【参考方案4】:

来自未来的讯息

对于在 2016 年遇到这些问题和答案的所有迷失者,有一个非常简单的解决方案也适用于多索引:

设置

id1 = ['A', 'B', 'C']
id2 = [1, 2]
identifiers = list(itertools.product(id1,id2))
identifier_names = ['one', 'two']
df = pd.DataFrame(identifiers, columns=identifier_names)
df['three'] = ['a','b','c','d','e','f']
df.set_index(identifier_names, inplace=True)
print(df)
        three
one two      
A   1       a
    2       b
B   1       c
    2       d
C   1       e
    2       f

解决方案

df = df.stack().unstack(identifier_names)
one    A     B     C   
two    1  2  1  2  1  2
three  a  b  c  d  e  f

希望能节省我花 3 个小时去发现的时间!

【讨论】:

以上是关于Pandas 在行上设置多索引,然后转置到列的主要内容,如果未能解决你的问题,请参考以下文章

将具有唯一值的列转置到行的 SQL 查询

如何从包含集合的 pandas 列转置和转换为“one-hot-encode”样式?

pandas 透视多索引列

Pandas Multiindex Groupby 列

使用 Pandas 从查找字典中重命名多索引行

将列中的大量地址(10000)信息列表转置到 csv 中,然后在 mysql 中上传