Python 确定缺少值的数据帧是否是另一个数据帧的子集

Python 确定缺少值的数据帧是否是另一个数据帧的子集,python,pandas,dataframe,Python,Pandas,Dataframe,我有两只熊猫。它们共享相同的列。第一个较大,并且不包含缺失值,例如 import pandas as pd import numpy as np df_full = pd.DataFrame({ "a": ["apple", "apple", "banana"], "b": [1, 2, 1] }) 第二行的行数较少,并且可能包含缺少的值 df_partial = pd.Dat

我有两只熊猫。它们共享相同的列。第一个较大,并且不包含缺失值,例如

import pandas as pd
import numpy as np

df_full = pd.DataFrame({
    "a": ["apple", "apple", "banana"],
    "b": [1, 2, 1]
})
第二行的行数较少,并且可能包含缺少的值

df_partial = pd.DataFrame({
    "a": ["apple", "apple"],
    "b": [np.nan, np.nan]
})
我想确定是否可以通过从
df_full
中删除行和元素并重新排序行来获得
df_partial
。或者换一种方式,我们可以将
df_partial
中的每一行匹配到
df_full
中的唯一行,其中一行匹配的条件是其非缺失元素相等

因此,在上述示例中,可以如上所述获得
df_partial
,因为我们可以将
df_partial
的前两行与
df_full
的前两行进行匹配(以任意顺序)

或者,数据帧

df_partial2 = pd.DataFrame({
    "a": ["banana"],
    "b": [2]
})
df_partial3 = pd.DataFrame({
    "a": ["apple", "apple", np.nan],
    "b": [1, 2, 2]
})
df_partial4 = pd.DataFrame({
    "a": ["apple", "apple"],
    "b": [np.nan, 1]
})
无法按说明获取,因为
df_full
中没有匹配的行

最后,还有一个稍微棘手的例子,dataframe

df_partial2 = pd.DataFrame({
    "a": ["banana"],
    "b": [2]
})
df_partial3 = pd.DataFrame({
    "a": ["apple", "apple", np.nan],
    "b": [1, 2, 2]
})
df_partial4 = pd.DataFrame({
    "a": ["apple", "apple"],
    "b": [np.nan, 1]
})
无法按说明获取,因为即使您可以将
df_partial3
中的每一行与
df_full
中的一行相匹配,也无法从
df_full
中选择唯一行来匹配
df_partial3
中的所有内容

其他一些考虑:

  • 这应该适用于任意数量的行/列
  • 您可以通过将所有可能的内射映射从
    df_partial
    行循环到
    df_full
    行来解决这个问题,但是如果可能的话,我想要更高效的东西
编辑:上面有一件事我没有说清楚。行的顺序并不重要。例如,数据帧

df_partial2 = pd.DataFrame({
    "a": ["banana"],
    "b": [2]
})
df_partial3 = pd.DataFrame({
    "a": ["apple", "apple", np.nan],
    "b": [1, 2, 2]
})
df_partial4 = pd.DataFrame({
    "a": ["apple", "apple"],
    "b": [np.nan, 1]
})

即使我们必须将
df_partial4
中的第二行与
df_full
中的第一行相匹配,并且将
df_partial4
中的第一行与
df_full

中的第二行相匹配,也可以按所述获得。
is_subset = (matching >= 0).all()
这是我最终使用的解决方案

TL;医生:

import pandas as pd
import numpy as np
from scipy.sparse.csgraph import maximum_bipartite_matching
from scipy.sparse import csr_matrix

def is_match(df_partial, df_full):
    full = df_full.to_numpy()
    partial = df_partial.to_numpy()
    nans = df_partial.isna().to_numpy()
    matches = (full[:, np.newaxis, :] == partial) | nans
    adjacency_matrix = matches.all(axis=2)
    matching = maximum_bipartite_matching(csr_matrix(adjacency_matrix))
    return (matching >= 0).all()

下面,我将使用问题中给出的第一个示例更详细地介绍这些步骤

首先,我们创建一个矩阵,其中元素i,j是
True
,如果
full\u df
的行i与
partial\u df
的行j匹配,否则为false

full = df_full.to_numpy()
partial = df_partial.to_numpy()
nans = df_partial.isna().to_numpy()

# Use numpy broadcasting to get a pairwise row comparison
matches = (full[:, np.newaxis, :] == partial) | nans
adjacency_matrix = matches.all(axis=2)
我们可以将其视为二部图的邻接矩阵,其中顶点是数据帧中的行,边位于匹配的行之间。我们想知道是否可以将
df_partial
中的每一行与
df_full
中的一行进行匹配。一个更一般的问题是,
df_partial
中我们可以在
df_full
中匹配的最大行数是多少

is_subset = (matching >= 0).all()
这个问题称为二部最大匹配问题,可以使用Hopcroft–Karp算法解决。据我所知,这是解决这个问题最有效的方法。在scipy中有一个实现

from scipy.sparse.csgraph import maximum_bipartite_matching
from scipy.sparse import csr_matrix


matching = maximum_bipartite_matching(csr_matrix(adjacency_matrix))
scipy函数,
maximum_bipartite_matching
,使用-1表示无法匹配的顶点,因此如果没有-1值,则
df_partial
df_full
的“子集”

is_subset = (matching >= 0).all()

查找最大匹配问题。没有更简单的解决方案。谢谢@user202729,我已经更新了问题,以指示行可能会被重新排列。@user202729谢谢指向最大匹配问题的指针-看起来我的问题是二分匹配