Pandas:使用多级索引数据进行透视

8

我有两个类似于以下结构的数据框:

rating
   BMW  Fiat  Toyota
0    7     2       3
1    8     1       8
2    9    10       7
3    8     3       9

own
   BMW  Fiat  Toyota
0    1     1       0
1    0     1       1
2    0     0       1
3    0     1       1

我最终想要得到一个关于品牌使用情况的平均评分数据透视表。或类似这样的内容:
            BMW  Fiat  Toyota
Usage                        
0      8.333333    10       3
1      7.000000     2       8

我的方法是将数据集合并如下:
Measure  Rating                Own              
Brand       BMW  Fiat  Toyota  BMW  Fiat  Toyota
0             7     2       3    1     1       0
1             8     1       8    0     1       1
2             9    10       7    0     0       1
3             8     3       9    0     1       1

尝试使用评分作为值、所有者作为行和品牌作为列来创建数据透视表,但我遇到了关键问题。我还尝试过取消堆叠度量或品牌级别,但似乎无法使用行索引名称作为数据透视表的键。我做错了什么?有更好的方法吗?
2个回答

4

我不是Pandas的专家,所以我的解决方案可能比您想要的更加笨拙,但是:

rating = pd.DataFrame({"BMW":[7, 8, 9, 8], "Fiat":[2, 1, 10, 3], "Toyota":[3, 8, 7,9]})
own = pd.DataFrame({"BMW":[1, 0, 0, 0], "Fiat":[1, 1, 0, 1], "Toyota":[0, 1, 1, 1]})

r = rating.unstack().reset_index(name='value')
o = own.unstack().reset_index(name='value')
res = DataFrame({"Brand":r["level_0"], "Rating": r["value"], "Own": o["value"]})
res = res.groupby(["Own", "Brand"]).mean().reset_index()
res.pivot(index="Own", columns="Brand", values="Rating")

# result
# Brand       BMW  Fiat  Toyota
# Own                          
# 0      8.333333    10       3
# 1      7.000000     2       8

另一种解决方案,虽然不太通用(你可以使用 for 循环,但必须知道 own 数据框中有哪些值):

d = []
for o in (0, 1):
    t = rating[own == o]
    t["own"] = o
    d.append(t)

res = pd.concat(d).groupby("own").mean()

谢谢。有解决方案真是太好了。你说得对,我本来希望有更优雅的解决方案,但是现在有了这个解决方案,我可以继续进行了。我总是可以编写一个函数。 - Brendon McLean
@Brendon 我现在正在尽可能多地学习Pandas,过一两周再看我能做什么 :) 请不要接受答案,也许会有一些大师提供超级优雅的解决方案。 - Roman Pekar
好的,你在个人资料上的标语已经说明了这一点 :). 我会再等一周才接受你的答案。再次感谢。 - Brendon McLean
@Brendon 看一下,我添加了另一个解决方案,我认为更符合 Python 风格。如果我知道如何在 DataFrame 中原地添加列,它甚至可以更短。 - Roman Pekar

3

我对自己的问题有了一个新的答案(基于Roman最初的回答)。关键是要获得所需维度的索引。例如

rating.columns.names = ["Brand"]
rating.index.names = ["n"]
print rating

Brand  BMW  Fiat  Toyota
n                       
0        7     2       3
1        8     1       8
2        9    10       7
3        8     3       9

own.columns.names = ["Brand"]
own.index.names = ["n"]
print own

Brand  BMW  Fiat  Toyota
n                       
0        1     1       0
1        0     1       1
2        0     0       1
3        0     1       1

merged = pd.merge(own.unstack().reset_index(name="Own"), 
                  rating.unstack().reset_index(name="Rating"))
print merged

     Brand  n  Own  Rating
0      BMW  0    1       7
1      BMW  1    0       8
2      BMW  2    0       9
3      BMW  3    0       8
4     Fiat  0    1       2
5     Fiat  1    1       1
6     Fiat  2    0      10
7     Fiat  3    1       3
8   Toyota  0    0       3
9   Toyota  1    1       8
10  Toyota  2    1       7
11  Toyota  3    1       9

然后很容易使用pivot_table命令将其转换为所需的结果:

print merged.pivot_table(rows="Brand", cols="Own", values="Rating")

Own             0  1
Brand               
BMW      8.333333  7
Fiat    10.000000  2
Toyota   3.000000  8

这正是我在寻找的。再次感谢Roman指路。


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