比较特定列的70%行

时间:2019-02-04 09:33:18

标签: python python-3.x pandas numpy dataframe

我想比较所有行中的特定列,或者所有行中70%的列,如果它们是唯一的,则将该值提取为0,否则将其提取到新列中。

如果示例数据框如下:

A      B           C  D  E  F  G  H  I  J  K  L   
32145  Basket      2  2  2  2  2  2  2  2  2  2
43290  Red ball    1  1  0  0  1  1  1  1  1  1
32891  wht ball    4  4  4  0  4  0  4  0  4  4
45328  grn ball    1  1  1  1  1  1  2  1  1  1
34531  blk ball    6  6  6  6  0  0  0  0  6  0

结果应如下:

  A      B         C  D  E  F  G  H  I  J  K  L  M    
32145  Basket      2  2  2  2  2  2  2  2  2  2  2 (100% of rows are similar)
43290  Red ball    1  1  0  0  1  1  1  1  1  1  1 (80% of rows are similar)
32891  wht ball    4  4  4  0  4  0  4  0  4  4  4 (70% of rows are similar)
45328  grn ball    1  1  1  1  1  1  2  1  1  1  1 (90% of rows are similar)
34531  blk ball    6  6  6  6  0  0  0  0  6  0  0 (only 50% of rows are similar) 

我使用以下答案来查找100%相似的行。
compare multiple specific columns of all rows
我想查找是否所有行中至少有70%相似,否则为0。
行数的考虑因素可能会有所变化,因为它并非一直都是恒定的。

我想要结果行中的唯一值(仅当且超过70%的行相似时),否则为0。(来自上面的示例“ M”列)

很高兴知道一些建议。

2 个答案:

答案 0 :(得分:3)

您可以通过scipy.stats.mode逐行计算模式,然后采用均值。本示例通过pd.Series.mask显式忽略0值。如果允许,不包括此蒙版(根据下面的基准测试),将会提高性能。

from scipy import stats

arr = df.iloc[:, 2:]
modes = stats.mode(arr.mask(arr.eq(0)), 1)[0].ravel()
df['ratio'] = arr.eq(modes, axis=0).mean(1)

print(df)

       A         B  C  D  E  F  G  H  I  J  K  L  ratio
0  32145    Basket  2  2  2  2  2  2  2  2  2  2    1.0
1  43290  Red ball  1  1  0  0  1  1  1  1  1  1    0.8
2  32891  wht ball  4  4  4  0  4  0  4  0  4  4    0.7
3  45328  grn ball  1  1  1  1  1  1  2  1  1  1    0.9
4  34531  blk ball  6  6  6  6  0  0  0  0  6  0    0.5

性能基准测试

对于大量的行与列,并使用类似算法,scipy.stats.mode的表现优于collections.Counter

from scipy.stats import mode
from collections import Counter

def counter_ratio(df):
    n= float(len(df.iloc[:, 2:].columns.values))
    df['ratio']=df.iloc[:, 2:].apply(lambda x: Counter(x.values).most_common(1)[0][1]/n,axis=1)
    return df

def mode_ratio(df):
    arr = df.iloc[:, 2:].values
    df['ratio'] = np.mean(arr == mode(arr, 1)[0], axis=1)
    return df

n = 10**4
df = pd.concat([df]*n, ignore_index=True)

%timeit counter_ratio(df.copy())  # 1.88 s per loop
%timeit mode_ratio(df.copy())     # 32.7 ms per loop

答案 1 :(得分:1)

尝试一下

n= float(len(df.iloc[:, 2:].columns.values))
df['ratio']=df.iloc[:, 2:].apply(lambda x: collections.Counter(x.values).most_common(1)[0][1]/n,axis=1)

输出:

       A         B  C  D  E  F  G  H  I  J  I.1  K  ratio
0  32145    Basket  2  2  2  2  2  2  2  2    2  2    1.0
1  43290  Red ball  1  1  0  0  1  1  1  1    1  1    0.8
2  32891  wht ball  4  4  4  0  4  0  4  0    4  4    0.7
3  45328  grn ball  1  1  1  1  1  1  2  1    1  1    0.9
4  34531  blk ball  6  6  6  6  0  0  0  0    6  0    0.5

效果指标:

df=(pd.concat([df]*10000,ignore_index=True))

我的建议解决方案:

start = time.time()
n= float(len(df.iloc[:, 2:].columns.values))
df['ratio']=df.iloc[:, 2:].apply(lambda x: collections.Counter(x.values).most_common(1)[0][1]/n,axis=1)
end = time.time()
print(end - start)

O/P: 0.7386555671691895

@jpp的解决方案:

start = time.time()
arr = df.iloc[:, 2:]
modes = stats.mode(arr.mask(arr.eq(0)), 1)[0].ravel()
df['ratio'] = arr.eq(modes, axis=0).mean(1)
end = time.time()
print(end - start)

O/P: 1.281557559967041

@Sandeep Kadapa的解决方案:

start = time.time()
d = (df.iloc[:, 2:].apply(pd.value_counts, 1).drop(0, 1).max(1)/df.iloc[:, 2:].shape[1])
df['L'] = np.where(d>0.5, df.iloc[:, 2:].max(1), 0)
end = time.time()
print(end - start)

O/P: 73.34089946746826