在部分匹配的索引上连接DataFrame

时间:2015-07-19 02:42:46

标签: join pandas

我正在尝试找到一种更优雅的方式来加入两个DataFrame,其中一个DF的索引级别是另一个DF的索引级别的部分子集。这是SQL中非常常见的操作,我很惊讶地发现使用pandas很难:

以下是一个例子:

import pandas as pd

df = pd.DataFrame(
    {
        2012:[4,5,8,9],
        2013:[1,2,4,7],
        2014:[6,5,4,3],
    },
    index= pd.MultiIndex.from_tuples([('apples',False),('bananas',False),('oranges',True),('lemons',True)], names=('fruit','citrus'))
)

=>

                2012  2013  2014
fruit   citrus                  
apples  False      4     1     6
bananas False      5     2     5
oranges True       8     4     4
lemons  True       9     7     3

[4 rows x 3 columns]

现在我想知道某一年销售的每种水果的最高数量:

fruit_max_by_date = df.max(axis=1).to_frame()
citrus_max_by_date = fruit_max_by_date.max(level='citrus')
citrus_max_by_date.columns = [1]

=>

fruit_max_by_date =

                    0
    fruit   citrus   
    apples  False   6
    bananas False   5
    oranges True    8
    lemons  True    9

    [4 rows x 1 columns]

citrus_max_by_date =

            1
    citrus   
    False   6
    True    9

    [2 rows x 1 columns]

到目前为止一直很好。但是现在我尝试将后两者加在一起:

fruit_max_by_date.join(citrus_max_by_date) =>

                    0   1
    fruit   citrus       
    apples  False   6 NaN
    bananas False   5 NaN
    oranges True    8 NaN
    lemons  True    9 NaN

    [4 rows x 2 columns]

Argh !! 因为第二个表的索引与第一个表的索引不完全匹配,所以连接失败。 这似乎完全违背了类似SQL的内连接的直观行为。

下面的所有解决方法(特别是第二个)都很难看,基本上涉及将索引抛出窗口,或者手动广播一个表的索引。 有更简单的方法吗?

解决方法:通过广播

扩展较小表的索引

这是我可以提出的最难看的解决方法,但它仍然非常糟糕,因为它需要扩展第二个阵列的大小,没有充分的理由。

fruit_max_by_date.join( 
  citrus_max_by_date.reindex(fruit_max_by_date.index, level='citrus') ) =>

                    0  1
    fruit   citrus      
    apples  False   6  6
    bananas False   5  6
    oranges True    8  9
    lemons  True    9  9

    [4 rows x 2 columns]

解决方法:截断第一个表的索引

这非常难看,特别是之后不得不重新组装索引,但它确实有效。

fruit_max_by_date                   \
    .reset_index(level='fruit')     \
    .join(citrus_max_by_date)       \
    .set_index('fruit',append=True  \
    .reorder_levels((1,0))          =>

                    0  1
    citrus fruit        
    False  apples   6  6
           bananas  5  6
    True   oranges  8  9
           lemons   9  9

    [4 rows x 2 columns]

放弃使用索引的所有借口,并在没有索引的情况下加入

好的,这是相对简单的,但是如果你不能使用索引,究竟是什么意思呢?

如果使用join - 而不是merge(FML !!) - 还有另一个奇怪的副作用:联接 - on列在输出中重复:

    fruit_max_by_date.reset_index().join(
       citrus_max_by_date.reset_index(),
       on='citrus', rsuffix='_' ) =>

             fruit citrus  0 citrus_  1
        0   apples  False  6   False  6
        1  bananas  False  5   False  6
        2  oranges   True  8    True  9
        3   lemons   True  9    True  9

        [4 rows x 5 columns]

    fruit_max_by_date.reset_index().merge(
       citrus_max_by_date.reset_index(),
       on='citrus' ) =>

             fruit citrus  0  1
        0   apples  False  6  6
        1  bananas  False  5  6
        2  oranges   True  8  9
        3   lemons   True  9  9

        [4 rows x 4 columns]

0 个答案:

没有答案