这是我问题的延续。 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

我试图在 A 的特定 10 列中找到 A 中的哪些行与 B 的每一行相对应。
A[My_Columns_List] 的每一行都保证在 B 中的某个位置,但并非 B 的每一行都与 A[My_Columns_List] 中的一行匹配

例如,我想表明对于 [B,D,E]A 列,

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

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

A 的行 [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 返回一个包含 A 的所有索引的数组,它没有告诉我任何事情。
我认为这个方法会更快,但我不知道为什么它返回一个从 0 到 999 的数组。
任何输入将不胜感激!

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

最佳答案

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

您要求比较 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]

python - 比较两个 Pandas 数据框的行?-LMLPHP
(0, 0) 位置 951 中的那个值意味着 A 第一行中的第一组 10 个 1 和 0 与 B 中具有索引 951 的行匹配。那就是你想要的!!!有趣的是,我从来没有看过 B。你知道为什么,B 无关紧要!!!这只是表示 0 到 1023 数字的一种愚蠢方式。这与我的答案不同,我忽略了 B 。忽略这个无用的步骤应该可以节省时间。

这些都是采用两个数据帧 AB 并返回索引数据帧的函数,其中 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()
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)

极简单衬
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 快了一个数量级

python - 比较两个 Pandas 数据框的行?-LMLPHP

10-08 06:55
查看更多