这有点难以解释,但请耐心听我解释,假设我们有以下数据集:
df = pd.DataFrame({'foo': [1, 1, 1, 8, 1, 5, 5, 5],
'bar': [2, float('nan'), 2, 5, 2, 3, float('nan'), 6],
'abc': [3, 3, 3, 7, float('nan'), 9, 9, 7],
'def': [4, 4, 4, 2, 4, 8, 8, 8]})
print(df)
>>>
foo bar abc def
0 1 2.0 3.0 4
1 1 NaN 3.0 4
2 1 2.0 3.0 4
3 8 5.0 7.0 2
4 1 2.0 NaN 4
5 5 3.0 9.0 8
6 5 NaN 9.0 8
7 5 6.0 7.0 8
我们的目标是查找所有重复的行。但是,其中一些重复的行是不完整的,因为它们具有NaN值。尽管如此,我们也希望查找这些重复的行。因此,预期结果为:
foo bar abc def
0 1 2.0 3.0 4
1 1 NaN 3.0 4
2 1 2.0 3.0 4
4 1 2.0 NaN 4
5 5 3.0 9.0 8
6 5 NaN 9.0 8
如果我们试图直接这样做,那么只能得到完整的行:
print(df[df.duplicated(keep=False)])
>>>
foo bar abc def
0 1 2.0 3.0 4
2 1 2.0 3.0 4
我们可以尝试通过只使用没有任何缺失值的列来绕过它:
print(df[df.duplicated(['foo', 'def'], keep=False)])
>>>
foo bar abc def
0 1 2.0 3.0 4
1 1 NaN 3.0 4
2 1 2.0 3.0 4
4 1 2.0 NaN 4
5 5 3.0 9.0 8
6 5 NaN 9.0 8
7 5 6.0 7.0 8
非常接近,但还不完全是。结果我们遗漏了“abc”列中的一条关键信息,它可以让我们确定第7行不是重复的。因此,我们希望包含它:
print(df[df.duplicated(['foo', 'def', 'abc'], keep=False)])
>>>
foo bar abc def
0 1 2.0 3.0 4
1 1 NaN 3.0 4
2 1 2.0 3.0 4
5 5 3.0 9.0 8
6 5 NaN 9.0 8
它成功地删除了第7行,但是也删除了第4行。NaN被认为是它自己的独立值,而不是可以等于任何值的值,因此它在第4行的存在使我们无法检测到这个重复值。
现在,我意识到我们并不确定第4行是否真的是[1,2,3,4],就我们所知,它可以是完全不同的值,比如[1,2,9,4],但是让我们假设值1和4实际上是一些奇怪的特定值,例如,34900和23893。假设还有更多的列也完全相同。此外,完全重复的行不仅仅是0和2,还有200多行,另外40行的所有列都具有相同的值,但“abc”除外。所以对于这个特殊的重复组,这种巧合是极不可能的,这就是为什么我们确定记录[1,2,3,4]是有问题的,第4行几乎肯定是重复的。
但是,如果[1,2,3,4]不是唯一的重复项组,则其他一些组的“foo”和“def”列中可能有非常不特定的值,如1和500。碰巧,在子集中包含“abc”列将非常有助于解决此问题,因为“abc”列中的值几乎总是非常特定的。并允许以近乎确定的方式确定所有重复项。但有一个缺点-“abc”列有缺失值,因此使用它会牺牲对一些NaN重复项的检测。其中一些我们知道是重复项(如前面提到的40个),因此这是一个艰难的困境。
处理这种情况的最佳方法是什么?如果我们能在重复检测期间使NaN等于所有值而不是零,这将解决这个问题,那将是很好的。但我怀疑这是可能的。我应该一组一组地手动检查吗?
2条答案
按热度按时间xqk2d5yq1#
感谢@cs95的帮助,当我们对值排序时,默认情况下NaN被放在排序组的末尾,如果不完整的记录中有一个与现有值重复的值,而不是这个NaN,它会在NaN的正上方结束。这意味着我们可以用
ffill()
方法用那个值填充NaN。用最接近的行的数据重新填充缺失的数据,这样我们就可以更准确地判断该行是否重复。我最终使用的代码(根据这个可重现的示例进行了调整)如下所示:
可以使用
bfill()
代替ffill()
,因为这是颠倒应用的相同原理,但需要将方法的一些默认参数改为相反的参数,即na_position='first'
和keep='last'
。sort_index()
只是用来消除重新索引警告。请注意,列的顺序非常重要,因为它用于排序优先级。要确保缺失值上方的记录是要复制的正确值,必须先枚举所有没有缺失值的列。并且只有那些具有多样性的列。对于前一列,顺序并不重要。对于后一列,从具有最多多样性的列开始是至关重要的/特定值,并以最少多样性/特定值结尾(float -〉int -〉string -〉bool是一个很好的经验法则,但它在很大程度上取决于列在数据集中表示的变量的确切类型)。在本例中,它们都是相同的,但即使在这里,如果您将'bar'放在'abc'之前,也不会得到正确的答案。
即便如此,这也不是一个完美的解决方案。它做得很好,将记录的最完整版本放在顶部,并在需要时将其中的信息转移到下面不太完整的版本。但也有可能记录的完全完整版本根本不存在。例如,假设有记录[53 Nans 8]和[5 NaN 98](也没有[5 3 9 8]记录),这个方案不能让他们交换丢失的片段,它会在前者中放入9,但在后者中NaN将保持为空,并且将导致这些副本不被注意。
如果只处理一个不完整的列,这不是问题,但是每添加一个不完整的列都会使这种情况越来越频繁。然而,添加所有列仍然是更可取的,因为未能检测到一些重复总比列表中出现一些虚假重复要好,除非使用所有列,否则这种可能性很大。
e7arh2l62#
很抱歉打扰您,但恐怕您的代码并不总是像预期的那样工作。
下面是一个例子:
结果: