注意:下面的帖子是我earlier question的“多键”对应物。该早期问题的解决方案仅适用于连接在单个密钥上的情况,并且我不清楚如何将这些解决方案推广到下面给出的多密钥情况。因为,IME修改一个已经回答的问题的方式使得它收到的答案不合格,所以在SO中我不赞成,我将单独发布这个变体。我还向Meta SO发布了一个question,关于我是否应该删除此帖子,而是修改原始问题,但代价是使其当前答案无效。
以下是我正在使用的更大/更复杂的数据帧的小型/玩具版本:
>>> A
key1 key2 u v w x
0 a G 0.757954 0.258917 0.404934 0.303313
1 b H 0.583382 0.504687 NaN 0.618369
2 c I NaN 0.982785 0.902166 NaN
3 d J 0.898838 0.472143 NaN 0.610887
4 e K 0.966606 0.865310 NaN 0.548699
5 f L NaN 0.398824 0.668153 NaN
key1 key2 y z
0 a G 0.867603 NaN
1 b H NaN 0.191067
2 c I 0.238616 0.803179
3 d G 0.080446 NaN
4 e H 0.932834 NaN
5 f I 0.706561 0.814467
(FWIW,在本文末尾,我提供了生成这些数据帧的代码。)
我想在key1
和key2
列上生成这些数据框的外连接,这样外连接引起的新位置的默认值为0.0。 IOW,期望的结果看起来像这样
key1 key2 u v w x y z
0 a G 0.757954 0.258917 0.404934 0.303313 0.867603 NaN
1 b H 0.583382 0.504687 NaN 0.618369 NaN 0.191067
2 c I NaN 0.982785 0.902166 NaN 0.238616 0.803179
3 d J 0.898838 0.472143 NaN 0.610887 0.000000 0.000000
4 e K 0.966606 0.86531 NaN 0.548699 0.000000 0.000000
5 f L NaN 0.398824 0.668153 NaN 0.000000 0.000000
6 d G 0.000000 0.000000 0.000000 0.000000 0.080446 NaN
7 e H 0.000000 0.000000 0.000000 0.000000 0.932834 NaN
8 f I 0.000000 0.000000 0.000000 0.000000 0.706561 0.814467
(请注意,此期望的输出包含一些NaN,即A
或B
中已存在的NaN。)
merge
方法让我分道扬but,但填充的默认值是NaN,而不是0.0:
>>> C = pandas.DataFrame.merge(A, B, how='outer', on=('key1', 'key2'))
>>> C
key1 key2 u v w x y z
0 a G 0.757954 0.258917 0.404934 0.303313 0.867603 NaN
1 b H 0.583382 0.504687 NaN 0.618369 NaN 0.191067
2 c I NaN 0.982785 0.902166 NaN 0.238616 0.803179
3 d J 0.898838 0.472143 NaN 0.610887 NaN NaN
4 e K 0.966606 0.865310 NaN 0.548699 NaN NaN
5 f L NaN 0.398824 0.668153 NaN NaN NaN
6 d G NaN NaN NaN NaN 0.080446 NaN
7 e H NaN NaN NaN NaN 0.932834 NaN
8 f I NaN NaN NaN NaN 0.706561 0.814467
fillna
方法无法产生所需的输出,因为它修改了一些应保持不变的位置:
>>> C.fillna(0.0)
key1 key2 u v w x y z
0 a G 0.757954 0.258917 0.404934 0.303313 0.867603 0.000000
1 b H 0.583382 0.504687 0.000000 0.618369 0.000000 0.191067
2 c I 0.000000 0.982785 0.902166 0.000000 0.238616 0.803179
3 d J 0.898838 0.472143 0.000000 0.610887 0.000000 0.000000
4 e K 0.966606 0.865310 0.000000 0.548699 0.000000 0.000000
5 f L 0.000000 0.398824 0.668153 0.000000 0.000000 0.000000
6 d G 0.000000 0.000000 0.000000 0.000000 0.080446 0.000000
7 e H 0.000000 0.000000 0.000000 0.000000 0.932834 0.000000
8 f I 0.000000 0.000000 0.000000 0.000000 0.706561 0.814467
如何有效地实现所需的输出? (性能在这里很重要,因为我打算在比这里显示的更大的数据帧上执行此操作。)
重要事项:为了使示例保持最小,我将多列只包含两列;实际上,多键中的键数可能要大得多。建议的答案应该适用于由至少六列组成的多键。
FWIW,下面是生成示例数据框A
和B
的代码。
from pandas import DataFrame
from collections import OrderedDict
from random import random, seed
def make_dataframe(rows, colnames):
return DataFrame(OrderedDict([(n, [row[i] for row in rows])
for i, n in enumerate(colnames)]))
maybe_nan = lambda: float('nan') if random() < 0.4 else random()
seed(0)
A = make_dataframe([['A', 'g', maybe_nan(), maybe_nan(), maybe_nan(), maybe_nan()],
['B', 'h', maybe_nan(), maybe_nan(), maybe_nan(), maybe_nan()],
['C', 'i', maybe_nan(), maybe_nan(), maybe_nan(), maybe_nan()],
['D', 'j', maybe_nan(), maybe_nan(), maybe_nan(), maybe_nan()],
['E', 'k', maybe_nan(), maybe_nan(), maybe_nan(), maybe_nan()],
['F', 'l', maybe_nan(), maybe_nan(), maybe_nan(), maybe_nan()]],
('key1', 'key2', 'u', 'v', 'w', 'x'))
B = make_dataframe([['A', 'g', maybe_nan(), maybe_nan()],
['B', 'h', maybe_nan(), maybe_nan()],
['C', 'i', maybe_nan(), maybe_nan()],
['D', 'g', maybe_nan(), maybe_nan()],
['E', 'h', maybe_nan(), maybe_nan()],
['F', 'i', maybe_nan(), maybe_nan()]],
('key1', 'key2', 'y', 'z'))
答案 0 :(得分:2)
将keys
设置为两个DF's
的索引:
def index_set(frame, keys=['key1', 'key2']):
frame.set_index(keys, inplace=True)
return frame
子集DF's
包含NaN
值:
def nulls(frame):
nulls_in_frame = frame[frame.isnull().any(axis=1)].reset_index()
return nulls_in_frame
加入两个Df's
。将已加入的DF
与包含NaN
的{{1}}的每个子集连接起来,并删除重复的值,将剩余的DF's
左侧填充为0。
然后,使用NaN
使用已加入combine_first
的链接操作来修补值。
DF
最后,
def perform_join(fr_1, fr_2, keys=['key1', 'key2']):
fr_1 = index_set(fr_1); frame_2 = index_set(fr_2)
frame = fr_1.join(fr_2, how='outer').reset_index()
cat_fr_1 = pd.concat([frame, nulls(fr_1)]).drop_duplicates(keys, keep=False).fillna(0)
cat_fr_2 = pd.concat([frame, nulls(fr_2)]).drop_duplicates(keys, keep=False).fillna(0)
fr_1_join = frame.combine_first(frame.fillna(cat_fr_1[fr_1.columns]))
joined_frame = fr_1_join.combine_first(frame.fillna(cat_fr_2[fr_2.columns]))
return joined_frame