Python Pandas DataFrame中的去重不会删除重复项

6

我有一个删除重复项的问题。我的程序基于循环生成元组(x,y),这些元组然后用作图中的节点。最终的节点数组/矩阵为:

[[ 1.          1.        ]
[ 1.12273268  1.15322175]
[..........etc..........]
[ 0.94120695  0.77802849]
**[ 0.84301344  0.91660517]**
[ 0.93096269  1.21383287]
**[ 0.84301344  0.91660517]**
[ 0.75506418  1.0798641 ]]

数组长度为22。现在,我需要移除重复的项(参见 **)。因此我使用了以下代码:

def urows(array):
    df = pandas.DataFrame(array)
    df.drop_duplicates(take_last=True)
    return df.drop_duplicates(take_last=True).values

很好,但我仍然遇到了以下问题:
           0         1
0   1.000000  1.000000
....... etc...........
17  1.039400  1.030320
18  0.941207  0.778028
**19  0.843013  0.916605**
20  0.930963  1.213833
**21  0.843013  0.916605**

所以去重并没有移除任何内容。我测试了一下,发现节点实际上是相同的,具体如下:
print urows(total_nodes)[19,:]
---> [ 0.84301344  0.91660517]
print urows(total_nodes)[21,:]
---> [ 0.84301344  0.91660517]
print urows(total_nodes)[12,:] - urows(total_nodes)[13,:]
---> [ 0.  0.]

为什么它不能工作?如何删除那些重复的值?

还有一个问题...

假设两个值“几乎”相等(比如x1和x2),有没有一种方法可以将它们以一种方式替换,使它们都相等?如果它们“几乎”相等,我想用x1替换x2。


drop_duplicates 确实 保留顺序,我不明白你在问什么...能否将这个问题简化一下? - Andy Hayden
谢谢。我完全编辑和重新构思了问题。我意识到我以错误的方式提出了错误的问题。 - Oniropolo
我不了解Panda,但是有可能a)条目在后面的小数位上不同,或者b)它们是两个不同的列表(恰好具有相同的条目),用于比较对象标识?如果这两种情况都不是,请忽略我的评论... - Elmar Peise
2个回答

6
如果我复制和粘贴你的数据,我会得到如下结果:
>>> df
          0         1
0  1.000000  1.000000
1  1.122733  1.153222
2  0.941207  0.778028
3  0.843013  0.916605
4  0.930963  1.213833
5  0.843013  0.916605
6  0.755064  1.079864

>>> df.drop_duplicates() 
          0         1
0  1.000000  1.000000
1  1.122733  1.153222
2  0.941207  0.778028
3  0.843013  0.916605
4  0.930963  1.213833
6  0.755064  1.079864

所以实际上它已经被移除了,你的问题是数组不是完全相等的(尽管它们的差异在显示时四舍五入到0)。

一种解决方法是使用类似于df.apply(np.round, args=[4])的方法将数据舍入到适用的小数位数,然后删除重复项。如果你想保留原始数据但删除在舍入上重复的行,可以使用类似于以下内容的东西:

df = df.ix[~df.apply(np.round, args=[4]).duplicated()]

以下是一种非常笨拙的方法,可以使接近相等的值变为实际相等的值:

grouped = df.groupby([df[i].round(4) for i in df.columns])
subbed = grouped.apply(lambda g: g.apply(lambda row: g.irow(0), axis=1))
subbed.drop_index(level=list(df.columns), drop=True, inplace=True)

这会重新排列数据框,但是如果需要的话,您可以调用.sort()将它们恢复为原始顺序。
解释:第一行使用groupby按舍入值对数据框进行分组。不幸的是,如果您将一个函数传递给groupby,它会将其应用于标签而不是行(因此您可能可以使用df.groupby(lambda k: np.round(df.ix[k], 4)),但那也很糟糕)。
第二行在groupby上使用apply方法,用新数据框g.apply(lambda row: g.irow(0), axis=1)替换近似重复行的数据框 g。这使用DataFrame的apply方法将每行替换为该组的第一行。
结果如下:
                        0         1
0      1                           
0.7551 1.0799 6  0.755064  1.079864
0.8430 0.9166 3  0.843013  0.916605
              5  0.843013  0.916605
0.9310 1.2138 4  0.930963  1.213833
0.9412 0.7780 2  0.941207  0.778028
1.0000 1.0000 0  1.000000  1.000000
1.1227 1.1532 1  1.122733  1.153222

groupby将四舍五入后的值作为索引插入。然后reset_index行会删除这些列。

希望有比我更懂pandas的人能来展示如何更好地完成此操作。


谢谢您的回答!当我尝试您的答案时,又有一个问题浮现在我的脑海中。如果x1和x2不完全相等,那么是否可以将x2更改为x1? - Oniropolo
你的意思是想要获取 df 并将其更改,以便几乎重复的内容不被删除,而是更改为实际重复的内容吗?我不确定如何立即做到这一点,除非使用 groupby 进行某些粗略操作。 - Danica
是的是的!我有可怕的舍入问题。我正在使用它来在图中生成节点,如果x1、x2不完全相等,则networkx将它们识别为不同的节点;如果x1=x2,则我得到所需的重组树。我可以用简单的if实现这一点,但运行时间是O(N^2),这会破坏一切。也许我应该把它作为一个新问题发布... - Oniropolo
我的最终目标是改变x1=x2,使它们完全相等,这将生成一个节点(而不是由四舍五入错误生成的两个节点)。下一步是删除重复项,然后再次运行代码以生成图形的下一步。 - Oniropolo

1
与@Dougal的答案类似,但略有不同。
In [20]: df.ix[~(df*1e6).astype('int64').duplicated(cols=[0])]
Out[20]: 
          0         1
0  1.000000  1.000000
1  1.122733  1.153222
2  0.941207  0.778028
3  0.843013  0.916605
4  0.930963  1.213833
6  0.755064  1.079864

谢谢你的回答!这里没有涉及到四舍五入,你只是改变了数据类型对吧? - Oniropolo
@MiguelHerschberg 将数字乘以一百万,然后转换为整数几乎等同于四舍五入到小数点后6位;不同之处在于这种方法总是向下取整。 - Danica
哦,太棒了。我能问你另一个问题吗?我有一个矩阵,其中一些值由于舍入误差而不完全相等。我想将这些不完全相等的值变成重复值。是否可以将每个条目乘以一百万,然后转换为整数?通过这种方式,可以获得重复的值,而不是几乎相等的值吗?谢谢! - Oniropolo
整数可以精确比较,浮点数有时候不行,因此如果你需要这种行为的话,最好使用整数。 - Jeff

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接