【问题标题】:Count files with duplicated checksum in Pandas在 Pandas 中计算具有重复校验和的文件
【发布时间】:2020-06-26 02:21:21
【问题描述】:

我想计算不同文件夹中的重复文件。这些文件具有不同的名称,因此我使用每个文件的校验和来跟踪重复项。这是样本数据

import pandas as pd

df = pd.DataFrame({
    'folder': ['d1', 'd1', 'd1', 'd2', 'd2', 'd2', 'd2', 'd2'],
    'checksum': ['x0', 'x1', 'x1', 'x1', 'x2', 'x2', 'x2', 'x3']})

df.head(10)

folder  checksum
d1      x0
d1      x1
d1      x1
d2      x1
d2      x2
d2      x2
d2      x2
d2      x3

这是我的解决方案,但我不喜欢它,因为它太长了。有谁知道使用Pandas.pivot_tablePandas.crosstab 之类的更好的方法来获得相同的结果?谢谢

def count_duplicated_checksum_among_folders(df):
    folders = df.folder.unique()
    result = []
    for fol1 in folders:
        row = []
        selected1 = df[(df.folder == fol1 ) & df.checksum.duplicated(keep=False)]
        dup_checksum = selected1.checksum.unique()
        for fol2 in folders:
            if fol1 == fol2 :
                total_dup = selected1.checksum.duplicated(keep=False).sum()
                row.append(total_dup)            
            else:
                selected2 = df[(df.folder == fol2) & df.checksum.isin(dup_checksum)]
                total_dup = selected2.checksum.unique().size
                row.append(total_dup)        
        result.append(row)
    return pd.DataFrame(result, columns=folders, index=folders)

预期输出

output = count_duplicated_checksum_among_folders(df)

    d1  d2
d1  2   1
d2  1   3

由于本例中有 2 个文件夹,因此预期输出为 2x2 矩阵

[[A, B]
 [C, D]]

A = total duplicates within folder d1
D = total duplicates within folder d2 
B = C = total duplicates that exist in both folders d1 and d2

理想的解决方案必须能够处理任意数量的文件夹。这是另一个包含 5 个文件夹的数据集。预期的答案也在下面给出。

folder = 'd1 d1 d1 d2 d2 d2 d2 d2 d3 d3 d1 d4 d1 d5'.split()
checksum = 'x0 x1 x1 x1 x2 x2 x2 x3 x4 x4 x4 x5 x5 x6'.split()
df2 = pd.DataFrame(dict(folder=folder, checksum=checksum))

    d1  d2  d3  d4  d5
d1  2   1   1   1   0
d2  1   3   0   0   0
d3  1   0   2   0   0
d4  1   0   0   0   0
d5  0   0   0   0   0

【问题讨论】:

  • x和d是什么关系?一条建议,没有人可能会阅读您的代码。对问题的简明描述或预期输出的列表会吸引快速答案。
  • @wwnde 我认为代码的工作版本描述了期望的结果,而不是输出。即使我不“阅读”它,我也可能想用它来检查我的解决方案。
  • @wwnde, x 是文件的校验和。如果文件的内容不同或被改变,它的校验和也会改变。文件被复制到不同的文件夹,因此存在重复。所以这个函数的目标是跟踪重复的计数和它们的文件夹位置S

标签: python pandas


【解决方案1】:

DataFrame.mergeDataFrame.pivot_tableSeries.nunique 一起使用:

df1 = (df.merge(df, on='checksum')
         .pivot_table(index='folder_x',
                     columns='folder_y', 
                     values='checksum', 
                     aggfunc='nunique',
                     fill_value=0)
         .rename_axis(index=None, columns=None))
print (df1)
    d1  d2  d3  d4  d5
d1   4   1   1   1   0
d2   1   3   0   0   0
d3   1   0   1   0   0
d4   1   0   0   1   0
d5   0   0   0   0   1

然后按每个folder 的总重复值数量按数量设置对角线:

s = (df[df.duplicated(['checksum','folder'], keep=False)]
      .groupby('folder')
      .size()
      .reindex(df['folder'].unique(), fill_value=0))
print (s)
folder
d1    2
d2    3
d3    2
d4    0
d5    0
dtype: int64

#https://stackoverflow.com/a/24475214/2901002
np.fill_diagonal(df1.values, s)
print (df1)
    d1  d2  d3  d4  d5
d1   2   1   1   1   0
d2   1   3   0   0   0
d3   1   0   2   0   0
d4   1   0   0   0   0
d5   0   0   0   0   0

验证:

output = count_duplicated_checksum_among_folders(df)
print (output)
    d1  d2  d3  d4  d5
d1   2   1   1   1   0
d2   1   3   0   0   0
d3   1   0   2   0   0
d4   1   0   0   0   0
d5   0   0   0   0   0

【讨论】:

  • 您的方法非常接近我的要求。它非常易读。但是如果有 n 个文件夹,你的代码可以处理任意数量的文件夹吗?谢谢!
  • @Scoodood C 我对其进行测试并与您的解决方案进行比较,输出不同。我认为差异应该是通过处理相同的重复值,而不是知道如果更多文件夹的预期输出。所以我建议使用更多文件夹创建数据样本,如果我的解决方案不起作用,我可以尝试更改它。谢谢。
  • 亲爱的@jezrael,我已经更新了我的原始问题以包含具有 3 个文件夹的数据集。我真的很喜欢您的答案,但是,您的代码产生的答案与我的不同。您的代码中缺少某些内容。
  • 非常有趣的解决方案,非常pythonic!只是喜欢它!!谢谢@jezrael
【解决方案2】:

如果您想使用上述数据框检查重复项以及哪个文件重复,您可以使用groupby

In [2]:
df.groupby(df.columns.tolist(),as_index=False).size()
Out[2]:
folder  checksum
d1      x0          1
        x1          2
d2      x1          1
        x2          3
        x3          1
dtype: int64

上面的代码基于所有列创建一个组,在您的情况下为folderchecksum,然后计算该组中每个不同值的计数。这将表明x1 校验和在d1 中是两次,在d2 中是x2 三次。

更新 2:

如果您需要计数,那么:

counter=df.groupby(df.columns.tolist(),as_index=False).size().unstack(fill_value=0)
counter_left=counter.ge(2,axis=0).dot(counter.T)
counter_right=counter.dot(counter.ge(2,axis=1).T)

#Now we merger left and right.
final=counter_left.add(counter.right) 
#and divide left diagonal element by 2
for diag in final:
    final.loc[diag,diag]//=2
print(final)

输出:

folder  d1  d2
folder        
d1       2   1
d2       1   3

counter_left 中,我首先使用counter.ge(2,axis=0) 找到了所有重复文件,其输出:

checksum     x0     x1     x2     x3
folder                              
d1        False   True  False  False
d2        False  False   True  False

然后用counter.T 进行点积得到输出:

folder  d1  d2
folder        
d1       2   1
d2       0   3

对 column_right 执行相同操作并将它们合并得到最终输出。

【讨论】:

  • 有趣的建议。我有超过 10k 的文件,我相信由于 checksum 列,您的输出列表将非常长,并且很难跟踪每个文件夹中的重复数。不过谢谢你的建议。
  • 嗨@ScoodoodC,我尝试了一种方法,它告诉你计数,我已经编辑了答案以包括你获得重复计数的更新。新的更新有用吗?
  • 如果此答案解决了您的问题,您能否通过单击其侧面的复选标记将其标记为已接受?谢谢你
  • 嗨@overflow95,感谢您的更新。但是,它仍然没有达到我的目标。我的预期答案是一个 2x2 矩阵 [[A, B][C, D]]。 A = 仅在 d1 中找到的总重复项; D = 仅在 d2 中发现的重复总数; B = C = d1 和 d2 中都存在的重复项。也许我的问题不清楚。我重新澄清了我的问题。再次感谢。
  • 根据您的新编辑进行了一些最终更改。希望对您有所帮助。
【解决方案3】:

试试:

df=df.assign(i=1).pivot_table(columns="folder", index="checksum", aggfunc="sum", values="i").fillna(0)

l=df.gt(1).T@df

r=df.T@df.gt(1)

res=pd.DataFrame([], columns=df.columns, index=df.columns)

res[:]=l+r

for col in res:
    res.loc[col, col]//=2

输出:

folder  d1  d2
folder
d1       2   1
d2       1   3

【讨论】:

  • 感谢您的建议。您的解决方案看起来也很干净。我用 5 个文件夹改进了我的最终测试用例。但你的答案与预期不同。
猜你喜欢
  • 2022-12-03
  • 2012-05-18
  • 2013-05-28
  • 2017-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多