我正在尝试找到一种更优雅的方式来加入两个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]