确保 Pandas 表中键的笛卡尔积出现

12

我有一个Pandas数据框,其中有两列关键字,我想确保这些关键字的笛卡尔积存在于表中(因为我将需要制作包含所有组合的二维图)。我在尝试找到一个简洁而习惯用语的方法来做这件事。

例如,我从以下表格开始,给出水果和蔬菜的组合以及它们在一起的味道:

   combo   fruit      veg
0  tasty   apple   carrot
1  yucky  banana   carrot
2  tasty  banana  lettuce
3  yucky   lemon  lettuce

我希望最终得到这样的表格,其中包含所有可能的组合:

    fruit      veg    combo
0   apple   carrot    tasty
1   apple  lettuce  UNKNOWN
2  banana   carrot    yucky
3  banana  lettuce    tasty
4   lemon   carrot  UNKNOWN
5   lemon  lettuce    yucky

这是我找到的最佳方法:

import pandas as pd

# Initial data
df=pd.DataFrame(dict(fruit=['apple','banana','banana','lemon'],
                     veg=['carrot','carrot','lettuce','lettuce'],
                     combo=['tasty','yucky','tasty','yucky']))

# Solution starts here
veg=df.groupby('veg').size().reset_index()
fruit=df.groupby('fruit').size().reset_index()
fruit[0] = veg[0] = 0    #use this dummy column for the join to work!
cartesian = pd.merge(fruit, veg, how='outer', on=0)
del cartesian[0]
all_combos = pd.merge(cartesian, df, how='left')
all_combos[ pd.isnull(all_combos.combo) ] = 'UNKNOWN'

我想一定有更简单且不容易出错的方法来做这件事... 有什么建议吗?

如果有人能向我展示如何使用包含fruitveg列的多索引进行操作,我将不胜感激,因为我确实对如何在索引中进行操作感到困惑。基于我的SQL经验,我认为这正是索引旨在解决的问题。

1个回答

13

在这个答案之后的某个时候,我将cartesian_product添加到pandas中,不久之后,MultiIndex.from_product被添加了(遵循另一个问题中的建议another question)。这使得以下简化更有效:

In [21]: p = pd.MultiIndex.from_product(df1.index.levels, names=df1.index.names)

In [22]: df1.reindex(p, fill_value='UNKNOWN')
Out[22]:
                  combo
fruit  veg
apple  carrot     tasty
       lettuce  UNKNOWN
banana carrot     yucky
       lettuce    tasty
lemon  carrot   UNKNOWN
       lettuce    yucky

旧答案如下:


如果你使用水果和蔬菜作为索引,那么可以使用itertools.product* 来创建 MultiIndex,然后通过reindex进行重建:

In [10]: from itertools import product

In [11]: df
Out[11]:
   combo   fruit      veg
0  tasty   apple   carrot
1  yucky  banana   carrot
2  tasty  banana  lettuce
3  yucky   lemon  lettuce

关键部分是获取所有可能的水果/蔬菜的正确MultiIndex:

In [12]: fruit_x_veg = list(product(np.unique(df['fruit']), np.unique(df['veg'])))

In [13]: fruit_x_veg = pd.MultiIndex.from_tuples(fruit_x_veg,
                                                 names=['fruit', 'veg'])

然后您可以按照这些重新索引:

In [14]: df1 = df.set_index(['fruit', 'veg'])

In [15]: df1
Out[15]:
                combo
fruit  veg
apple  carrot   tasty
banana carrot   yucky
       lettuce  tasty
lemon  lettuce  yucky

In [16]: df1.reindex(fruit_x_veg, fill_value='UNKNOWN')
Out[16]:
                  combo
fruit  veg
apple  carrot     tasty
       lettuce  UNKNOWN
banana carrot     yucky
       lettuce    tasty
lemon  carrot   UNKNOWN
       lettuce    yucky

* 如果itertools.product不够快,请考虑使用这个numpy实现

注意:这个实现已经在pandas.tools.util.cartesian_product中扩展,现在支持更多的数据类型(并且在MultiIndex.from_product内部使用)。


谢谢,安迪,这对我很有效。看起来reindex还有一个可选参数fill_value,可以节省一步操作(例如:df1.reindex(fruit_x_veg, fill_value='UNKNOWN'))。 - Dan Lenski
1
@Dan 很棒!哦,好整齐 :) - Andy Hayden
1
@Dan FYI,最近有一个github问题关于本地添加此功能的问题。 - Andy Hayden
请注意,在当前版本的pandas中,cartesian_product位于pandas.core.reshape.util(而不是pandas.tools.util)中。 - BeingQuisitive

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