比较两个pandas数据帧的行?

时间:2016-07-21 02:17:45

标签: python pandas numpy dataframe

这是我的问题的延续。 Fastest way to compare rows of two pandas dataframes?

我有两个数据框AB

A是1000行x 500列,填充二进制值,表示是否存在。

对于一个简明的例子:

    A   B   C   D   E  
0   0   0   0   1   0  
1   1   1   1   1   0  
2   1   0   0   1   1  
3   0   1   1   1   0  

B是1024行x 10列,是从二进制形式的0到1023的完整迭代。

示例:

     0   1   2  
0    0   0   0  
1    0   0   1   
2    0   1   0  
3    0   1   1  
4    1   0   0
5    1   0   1
6    1   1   0 
7    1   1   1

我正在尝试查找AA的特定10列的哪些行与B的每一行对应。

A[My_Columns_List]的每一行都保证在B的某个位置,但B的每一行都不会与A[My_Columns_List]

中的一行匹配

例如,我想为[B,D,E]的<{1}}列显示

A的行A[1,3]的行[6]匹配,

A的

B[0]的行[2]匹配,

A的

B[2]的行[3]匹配。

我尝试过使用:

B

这有效,但我希望这种方法更快:

pd.merge(B.reset_index(), A.reset_index(),
left_on = B.columns.tolist(),
right_on =A.columns[My_Columns_List].tolist(), 
suffixes = ('_B','_A')))

但是当我这样做时,S = 2**np.arange(10) A_ID = np.dot(A[My_Columns_List],S) B_ID = np.dot(B,S) out_row_idx = np.where(np.in1d(A_ID,B_ID))[0] 会返回一个包含out_row_idx所有索引的数组,但这些索引并没有告诉我任何事情。 我认为这种方法会更快,但我不知道为什么它返回一个从0到999的数组。 任何意见都将不胜感激!

此外,这些方法还归功于@jezrael和@Divakar。

1 个答案:

答案 0 :(得分:6)

我会坚持我最初的答案,但也许可以解释得更好。

您要求比较2个pandas数据帧。因此,我将构建数据帧。我可能会使用numpy,但我的输入和输出将是数据帧。

设置

你说我们有一个1000 x 500的1和0数组。让我们构建它。

A_init = pd.DataFrame(np.random.binomial(1, .5, (1000, 500)))
A_init.columns = pd.MultiIndex.from_product([range(A_init.shape[1]/10), range(10)])
A = A_init

此外,我提供了A一个MultiIndex,可以按10列轻松分组。

解决方案

这与@Divakar的答案非常相似,只有一点不同,我会指出。

对于一组10个1和0,我们可以将它视为长度为8的位数组。然后我们可以通过使用幂积为2的点积来计算它的整数值。

twos = 2 ** np.arange(10)

我可以像这样对每一组10个1和0执行此操作

AtB = A.stack(0).dot(twos).unstack()

stack将50个10组的行放入列中,以便更优雅地完成点积。然后我用unstack将其带回来。

我现在有一个1000 x 50的数字框架,数字范围是0-1023。

假设B是一个数据帧,每行包含1024个唯一的1和0组合之一。 B应按B = B.sort_values().reset_index(drop=True)排序。

这是我认为上次解释失败的部分。看看

AtB.loc[:2, :2]

enter image description here

(0, 0)位置中的值951表示A第一行中的第一组10个1和0与B中的行匹配索引951。那正是你想要的!!!有趣的是,我从未看过B.你知道为什么,B无关紧要!这只是一种表达从0到1023的数字的愚蠢方式。这与我的答案不同,我忽略了B。忽略这个无用的步骤应该可以节省时间。

这些功能包含两个数据帧AB,并返回AB匹配的索引数据帧。剧透警告,我将完全忽略B

def FindAinB(A, B):
    assert A.shape[1] % 10 == 0, 'Number of columns in A is not a multiple of 10'
    rng = np.arange(A.shape[1])
    A.columns = pd.MultiIndex.from_product([range(A.shape[1]/10), range(10)])

    twos = 2 ** np.arange(10)

    return A.stack(0).dot(twos).unstack()
def FindAinB2(A, B):
    assert A.shape[1] % 10 == 0, 'Number of columns in A is not a multiple of 10'
    rng = np.arange(A.shape[1])
    A.columns = pd.MultiIndex.from_product([range(A.shape[1]/10), range(10)])
    # use clever bit shifting instead of dot product with powers
    # questionable improvement
    return (A.stack(0) << np.arange(10)).sum(1).unstack()

我正在引导我的内心@Divakar(请阅读,这是我从Divakar学到的东西)

def FindAinB3(A, B):
    assert A.shape[1] % 10 == 0, 'Number of columns in A is not a multiple of 10'
    a = A.values.reshape(-1, 10)
    a = np.einsum('ij->i', a << np.arange(10))
    return pd.DataFrame(a.reshape(A.shape[0], -1), A.index)

Minimalist One Liner

f = lambda A: pd.DataFrame(np.einsum('ij->i', A.values.reshape(-1, 10) << np.arange(10)).reshape(A.shape[0], -1), A.index)

一样使用它
f(A)

时序

FindAinB3快一个数量级

enter image description here