这是我的问题的延续。 Fastest way to compare rows of two pandas dataframes?
我有两个数据框A
和B
:
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
中A
的特定10列的哪些行与B
的每一行对应。
A[My_Columns_List]
的每一行都保证在B
的某个位置,但B
的每一行都不会与A[My_Columns_List]
例如,我想为[B,D,E]
的<{1}}列显示
A
与[1,3]
的行[6]
匹配,
A的行B
与[0]
的行[2]
匹配,
行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。
答案 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]
(0, 0)
位置中的值951
表示A
第一行中的第一组10个1和0与B
中的行匹配索引951
。那正是你想要的!!!有趣的是,我从未看过B.你知道为什么,B无关紧要!这只是一种表达从0到1023的数字的愚蠢方式。这与我的答案不同,我忽略了B
。忽略这个无用的步骤应该可以节省时间。
这些功能包含两个数据帧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()
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快一个数量级