比较两个pandas数据帧的行?

Gar*_*ler 4 python numpy dataframe pandas

这是我的问题的延续.比较两个pandas数据帧行的最快方法?

我有两个数据帧AB:

A 是1000行×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  
Run Code Online (Sandbox Code Playgroud)

B 是1024行×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
Run Code Online (Sandbox Code Playgroud)

我试图找到A特定10列的哪些行A与每行对应B.

每一行都A[My_Columns_List]保证在某个地方B,但并不是每一行都B与一行匹配A[My_Columns_List]

例如,我想显示列[B,D,E]A,

[1,3]的匹配了行[6]B,

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

[2]A的匹配了行[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')))
Run Code Online (Sandbox Code Playgroud)

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

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]
Run Code Online (Sandbox Code Playgroud)

但是当我这样做时,out_row_idx返回一个包含所有索引的数组A,这并没有告诉我什么.我认为这个方法会更快,但我不知道为什么它返回一个从0到999的数组.任何输入都将不胜感激!

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

piR*_*red 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
Run Code Online (Sandbox Code Playgroud)

另外,我给了A一个MultiIndex容易按10列的分组.

这与@Divakar的回答非常类似,我将指出一个小的差异.

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

twos = 2 ** np.arange(10)
Run Code Online (Sandbox Code Playgroud)

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

AtB = A.stack(0).dot(twos).unstack()
Run Code Online (Sandbox Code Playgroud)

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]
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

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

这些都是需要两个dataframes的所有功能A,并B返回指数,其中的数据帧A匹配B.剧透警报,我会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()
Run Code Online (Sandbox Code Playgroud)
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()
Run Code Online (Sandbox Code Playgroud)

我正在引导我的内心@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)
Run Code Online (Sandbox Code Playgroud)

极简主义的一个班轮

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

像它一样使用它

f(A)
Run Code Online (Sandbox Code Playgroud)

定时

FindAinB3速度提高了一个数量级

在此输入图像描述