Удаление дубликатов на основе правила большинства - PullRequest
0 голосов
/ 15 мая 2018

У меня есть таблица, которая выглядит так:

A  B
1  cat
1  cat
1  dog
2  illama
2  alpaca
3  donkey

Используя A в качестве ключа, я хотел бы удалить дубликаты так, чтобы кадр данных стал:

A  B
1  cat
3  donkey

1 дублируется три раза, значение cat встречается чаще всего, поэтому оно записывается. для 2 большинства нет, поэтому он считается неоднозначным и полностью исключен. 3 остается, поскольку у него нет дубликатов.

Ответы [ 3 ]

0 голосов
/ 15 мая 2018

Вы можете использовать statistics.mode, что повышает StatisticsError, если не существует уникального режима.

from statistics import mode, StatisticsError

def moder(x):
    try:
        return mode(x)
    except StatisticsError:
        return None

res = df.groupby('A')['B'].apply(moder)\
        .dropna().reset_index()

print(res)

   A       B
0  1     cat
1  3  donkey

Сравнение производительности

Хотя все 3 метода подходят для вашей задачи, они имеют незначительную разницу в производительности.

Результаты бенчмаркинга:

df = pd.concat([df]*10000)

%timeit jpp(df)  # 18.3 ms ± 414 µs per loop
%timeit cs1(df)  # 28.1 ms ± 558 µs per loop
%timeit cs2(df)  # 24.5 ms ± 595 µs per loop

Код бенчмаркинга:

from statistics import mode, StatisticsError

def moder(x):
    try:
        return mode(x)
    except StatisticsError:
        return None

def foo(x):
    m = pd.Series.mode(x)
    if len(m) == 1: 
        return m

def jpp(df):
    return df.groupby('A')['B'].apply(moder)\
             .dropna().reset_index()

def cs1(df):
    i = df.groupby('A').B.apply(pd.Series.mode).reset_index(level=1, drop=True)
    return i[i.groupby(level=0).transform('count') == 1].reset_index()

def cs2(df):
    return df.groupby('A').B.apply(foo).reset_index(level=1, drop=True).reset_index()
0 голосов
/ 15 мая 2018

Как насчет использования value_counts и rank:

df.groupby('A')['B'].apply(lambda x: x.value_counts().rank(ascending=False)).eq(1)[lambda x: x].reset_index()

Выход:

   A level_1     B
0  1     cat  True
1  3  donkey  True

Использование ранга с методом = "средний" в качестве избирателя для получения "большинства" из результатов value_count.

0 голосов
/ 15 мая 2018

groupby + pd.Series.mode

Это двухэтапное решение с использованием pd.Series.mode:

# find the mode for each group
i = df.groupby('A').B.apply(pd.Series.mode).reset_index(level=1, drop=True)
# filter out groups which have more than one mode—ambiguous groups
j = i[i.groupby(level=0).transform('count') == 1].reset_index()

print(j)

   A       B
0  1     cat
1  3  donkey

groupby + < custom func >

В качестве альтернативы, определите пользовательскую функцию, которая вычисляет режим, и вызовите ее с помощью apply.Логика фильтрации включена в функцию.

def foo(x):
    m = pd.Series.mode(x)
    if len(m) == 1: 
        return m

df.groupby('A').B.apply(foo).reset_index(level=1, drop=True).reset_index()

   A       B
0  1     cat
1  3  donkey
...