如何计算每列中的唯一重复值



我们有以下数据帧,

df = pd.DataFrame(data = {'A': [1,2,3,3,2,4,5,3],
'B': [9,6,7,9,2,5,3,3],
'C': [4,4,4,5,9,3,2,1]})
df

我想创建一个新的数据帧,其中每个列名称将显示重复项的数量。

例如。'B',有两个重复的值(9 和 3),我想打印 2 等

选项 1

如果我们需要计算重复值的数量

import pandas as pd
df = pd.DataFrame(data = {'A': [1,2,3,3,2,4,5,3],
'B': [9,6,7,9,2,5,3,3],
'C': [4,4,4,5,9,3,2,1]})
df1 = df.apply(lambda x:sum(x.duplicated()))
print(df1)

指纹:

A    3
B    2
C    2
dtype: int64

选项 2

如果我们需要计算具有重复项的值的数量

df1 = df.agg(lambda x: sum(x.value_counts() > 1)) # or df1 = df.apply(lambda x: sum(x.value_counts() > 1))
print(df1)

指纹:

A    2
B    2
C    1
dtype: int64

选项 2.1

详细

df1 = df.apply(lambda x: ' '.join([f'[val = {i}, cnt = {v}]' for i, v in x.value_counts().iteritems() if v > 1]))
print(df1)

指纹:

A    [val = 3, cnt = 3] [val = 2, cnt = 2]
B    [val = 9, cnt = 2] [val = 3, cnt = 2]
C                       [val = 4, cnt = 3]
dtype: object

如果你想让每个元素重复计数,你可以使用这个:

import pandas as pd
from collections import Counter
df = pd.DataFrame(data = {'A': [1,2,3,3,2,4,5,3],
'B': [9,6,7,9,2,5,3,3],
'C': [4,4,4,5,9,3,2,1]})
def cnt(x):
return {k:v for k,v in x.items() if v>1}

df.apply(lambda x : cnt(Counter(x)))

输出:

A    {2: 2, 3: 3}
B    {9: 2, 3: 2}
C          {4: 3}
dtype: object
  • 这可以通过获取每列的pandas.Series.value_counts来完成,然后获取值计数大于 1 的pandas.Series.sum
    • vc[vc.gt(1)]创建一个包含计数的pandas.Series,对于列中的每个值,计数大于 1。
  • 我们可以从 5 列 1M 行的%%timeit比较中看到,.apply使用矢量化方法以及for-loopdict-comprehension,比使用带有内置 pythonsum(...).apply更快。

.apply.value_counts.sum

  • col.value_counts().gt(1)创建Boolean系列
    • True的计算结果为 1,False计算为 0,因此.sum()会产生正确的结果。
dupe_count = df.agg(lambda col: col.value_counts().gt(1).sum())
A    2
B    2
C    1
dtype: int64

for-loop

  • 通常不建议循环访问数据帧,尤其是按行循环访问。但是,我们正在遍历列,然后应用矢量化函数,这相当于.apply发生的情况。
def col_vc(df):
dupe_count = dict()
for col in df.columns:
dupe_count[col] = df[col].value_counts().gt(1).sum()
return dupe_count

col_vc(df)
[result]:
{'A': 2, 'B': 2, 'C': 1}
  • 等效的单行dict-comprehension
dupe_count = {col: df[col].value_counts().gt(1).sum() for col in df.columns}
[result]:
{'A': 2, 'B': 2, 'C': 1}
# to a dataframe if desired
dupe_count = pd.DataFrame.from_dict(dupe_count, orient='index')
0
A  2
B  2
C  1

%%timeit比较

import pandas as pd
import numpy as np
# sample data 5 columns by 1M rows
np.random.seed(365)
rows = 1000000
data = {'a': np.random.randint(0, 10000, size=(rows)),
'b': np.random.randint(15, 25000, size=(rows)),
'c': np.random.randint(30, 40000, size=(rows)),
'd': np.random.randint(450, 550000, size=(rows)),
'e': np.random.randint(6000, 70000, size=(rows))}
df = pd.DataFrame(data)
  • .apply.value_counts.sum
%%timeit
df.agg(lambda x: x.value_counts().gt(1).sum())
[out]:
112 ms ± 1.67 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
  • dict-comprehension
%%timeit
{col: df[col].value_counts().gt(1).sum() for col in df.columns}
[out]:
111 ms ± 983 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
  • for-loop
%%timeit
col_vc(df)
[out]:
115 ms ± 4.11 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
  • .applysum()
%%timeit
df.agg(lambda x: sum(x.value_counts() > 1))
[out]:
194 ms ± 17.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

您可以使用collections.Counteritertools.takewhile

from collections import Counter
from itertools import takewhile
df.apply(lambda c: len(list(takewhile(lambda x: x[1]>1, Counter(c).most_common()))))

输出:

A    2
B    2
C    1

如果要将输出作为数据帧,请添加.to_frame(name='n_duplicates'): 输出:

n_duplicates
A             2
B             2
C             1

工作原理

对于每一列,Counter获取每个元素的计数,most_common首先返回它们。

takewhile迭代此输入,并在阈值以下有一个元素(此处为 1)时立即停止。

最后,我们得到这个输出的长度,它对应于重复组的数量。

最新更新