Pandas:在两个数据框中找到匹配的行(不使用 `merge`)
Pandas: find matching rows in two dataframes (without using `merge`)
假设我有这两个列数相同但行数可能不同的数据框:
tmp = np.arange(0,12).reshape((4,3))
df = pd.DataFrame(data=tmp)
tmp2 = {'a':[3,100,101], 'b':[4,4,100], 'c':[5,100,3]}
df2 = pd.DataFrame(data=tmp2)
print(df)
0 1 2
0 0 1 2
1 3 4 5
2 6 7 8
3 9 10 11
print(df2)
a b c
0 3 4 5
1 100 4 100
2 101 100 3
我想验证 df2
的行是否与 df
的任何行匹配,也就是说我想获得一系列(或数组)布尔值来给出这个结果:
0 True
1 False
2 False
dtype: bool
我认为类似 isin
的方法应该有效,但我得到了这个结果,它产生了一个数据帧并且是错误的:
print(df2.isin(df))
a b c
0 False False False
1 False False False
2 False False False
作为约束,我不希望使用 merge
方法,因为我所做的实际上是在应用合并本身之前检查数据。
感谢您的帮助!
可能有更有效的解决方案,但您可以附加两个数据帧可以调用 duplicated
,例如:
df.append(df2).duplicated().iloc[df.shape[0]:]
这假设每个 DataFrame 中的所有行都是不同的。以下是一些基准:
tmp1 = np.arange(0,12).reshape((4,3))
df1 = pd.DataFrame(data=tmp1, columns=["a", "b", "c"])
tmp2 = {'a':[3,100,101], 'b':[4,4,100], 'c':[5,100,3]}
df2 = pd.DataFrame(data=tmp2)
df1 = pd.concat([df1] * 10_000).reset_index()
df2 = pd.concat([df2] * 10_000).reset_index()
%timeit df1.append(df2).duplicated().iloc[df1.shape[0]:]
# 100 loops, best of 5: 4.16 ms per loop
%timeit pd.Series([m.all() for m in np.isin(df2.values,df1.values)])
# 10 loops, best of 5: 74.9 ms per loop
%timeit df2.apply(frozenset, axis=1).isin(df1.apply(frozenset, axis=1))
# 1 loop, best of 5: 443 ms per loop
尝试:
df[~df.apply(tuple,1).isin(df2.apply(tuple,1))]
这是我的结果:
使用np.in1d
:
>>> df2.apply(lambda x: all(np.in1d(x, df)), axis=1)
0 True
1 False
2 False
dtype: bool
另一种方式,使用frozenset
:
>>> df2.apply(frozenset, axis=1).isin(df1.apply(frozenset, axis=1))
0 True
1 False
2 False
dtype: bool
您可以使用 MultiIndex(昂贵的 IMO):
pd.MultiIndex.from_frame(df2).isin(pd.MultiIndex.from_frame(df))
Out[32]: array([ True, False, False])
另一种选择是创建字典,运行 isin
:
df2.isin({key : array.array for key, (_, array) in zip(df2, df.items())}).all(1)
Out[45]:
0 True
1 False
2 False
dtype: bool
您可以使用 numpy.isin
,它将比较您数组中的所有元素,并且 return True
或 False
比较每个数组的每个元素。
然后在每个数组上使用 all()
,如果所有元素都为真,将得到您想要的输出作为函数 returns True
:
>>> pd.Series([m.all() for m in np.isin(df2.values,df.values)])
0 True
1 False
2 False
dtype: bool
正在发生的事情的细分:
# np.isin
>>> np.isin(df2.values,df.values)
Out[139]:
array([[ True, True, True],
[False, True, False],
[False, False, True]])
# all()
>>> [m.all() for m in np.isin(df2.values,df.values)]
Out[140]: [True, False, False]
# pd.Series()
>>> pd.Series([m.all() for m in np.isin(df2.values,df.values)])
Out[141]:
0 True
1 False
2 False
dtype: bool
假设我有这两个列数相同但行数可能不同的数据框:
tmp = np.arange(0,12).reshape((4,3))
df = pd.DataFrame(data=tmp)
tmp2 = {'a':[3,100,101], 'b':[4,4,100], 'c':[5,100,3]}
df2 = pd.DataFrame(data=tmp2)
print(df)
0 1 2
0 0 1 2
1 3 4 5
2 6 7 8
3 9 10 11
print(df2)
a b c
0 3 4 5
1 100 4 100
2 101 100 3
我想验证 df2
的行是否与 df
的任何行匹配,也就是说我想获得一系列(或数组)布尔值来给出这个结果:
0 True
1 False
2 False
dtype: bool
我认为类似 isin
的方法应该有效,但我得到了这个结果,它产生了一个数据帧并且是错误的:
print(df2.isin(df))
a b c
0 False False False
1 False False False
2 False False False
作为约束,我不希望使用 merge
方法,因为我所做的实际上是在应用合并本身之前检查数据。
感谢您的帮助!
可能有更有效的解决方案,但您可以附加两个数据帧可以调用 duplicated
,例如:
df.append(df2).duplicated().iloc[df.shape[0]:]
这假设每个 DataFrame 中的所有行都是不同的。以下是一些基准:
tmp1 = np.arange(0,12).reshape((4,3))
df1 = pd.DataFrame(data=tmp1, columns=["a", "b", "c"])
tmp2 = {'a':[3,100,101], 'b':[4,4,100], 'c':[5,100,3]}
df2 = pd.DataFrame(data=tmp2)
df1 = pd.concat([df1] * 10_000).reset_index()
df2 = pd.concat([df2] * 10_000).reset_index()
%timeit df1.append(df2).duplicated().iloc[df1.shape[0]:]
# 100 loops, best of 5: 4.16 ms per loop
%timeit pd.Series([m.all() for m in np.isin(df2.values,df1.values)])
# 10 loops, best of 5: 74.9 ms per loop
%timeit df2.apply(frozenset, axis=1).isin(df1.apply(frozenset, axis=1))
# 1 loop, best of 5: 443 ms per loop
尝试:
df[~df.apply(tuple,1).isin(df2.apply(tuple,1))]
这是我的结果:
使用np.in1d
:
>>> df2.apply(lambda x: all(np.in1d(x, df)), axis=1)
0 True
1 False
2 False
dtype: bool
另一种方式,使用frozenset
:
>>> df2.apply(frozenset, axis=1).isin(df1.apply(frozenset, axis=1))
0 True
1 False
2 False
dtype: bool
您可以使用 MultiIndex(昂贵的 IMO):
pd.MultiIndex.from_frame(df2).isin(pd.MultiIndex.from_frame(df))
Out[32]: array([ True, False, False])
另一种选择是创建字典,运行 isin
:
df2.isin({key : array.array for key, (_, array) in zip(df2, df.items())}).all(1)
Out[45]:
0 True
1 False
2 False
dtype: bool
您可以使用 numpy.isin
,它将比较您数组中的所有元素,并且 return True
或 False
比较每个数组的每个元素。
然后在每个数组上使用 all()
,如果所有元素都为真,将得到您想要的输出作为函数 returns True
:
>>> pd.Series([m.all() for m in np.isin(df2.values,df.values)])
0 True
1 False
2 False
dtype: bool
正在发生的事情的细分:
# np.isin
>>> np.isin(df2.values,df.values)
Out[139]:
array([[ True, True, True],
[False, True, False],
[False, False, True]])
# all()
>>> [m.all() for m in np.isin(df2.values,df.values)]
Out[140]: [True, False, False]
# pd.Series()
>>> pd.Series([m.all() for m in np.isin(df2.values,df.values)])
Out[141]:
0 True
1 False
2 False
dtype: bool