Как оптимизировать этот метод голосования большинством голосов

У меня есть следующий код для большинства голосов за данные в фреймворке данных:

def vote(df, systems):
    test = df.drop_duplicates(subset=['begin', 'end', 'case', 'system'])
    n = int(len(systems)/2)
    data = []
        
    for row in test.itertuples():
        # get all matches
        fx = test.loc[(test.begin == row.begin) & (test.end == row.end) & (test.case == row.case)]
        fx = fx.loc[fx.system.isin(systems)]

        # keep if in a majority of systems   
        if len(set(fx.system.tolist())) > n:
            data.append(fx)
             
    out = pd.concat(data, axis=0, ignore_index=True)
    out = out.drop_duplicates(subset=['begin', 'end', 'case'])
    
    return out[['begin', 'end', 'case']]  

Данные выглядят так:

systems = ['A', 'B', 'C', 'D', 'E']

df = begin,end,system,case
0,9,A,0365
10,14,A,0365
10,14,B,0365
10,14,C,0365
28,37,A,0366
38,42,A,0366
38,42,B,0366
53,69,C,0366
56,60,B,0366
56,60,C,0366
56,69,D,0366
64,69,E,0366
83,86,B,0367

Ожидаемый результат должен быть:

out = begin,end,case
    10,14,0365
    56,69,0366

IOW, при желании элементы begin, end, case появляются в большинстве систем, мы накапливаем их и возвращаем как фрейм данных.

Алгоритм работает отлично, но, поскольку в нем сотни тысяч строк, его обработка занимает довольно много времени.

Одна оптимизация, которую я могу придумать, но не знаю, как ее реализовать, находится в itertuples итерация: если для первого экземпляра набора фильтров begin, end, case есть совпадения в

fx = test.loc[(test.begin == row.begin) & (test.end == row.end) & (test.case == df.case) & (fx.system.isin(systems))]

тогда было бы полезно не перебирать другие строки в itertuples итерация, совпадающая с этим фильтром. Например, для первого экземпляра 10,14,A,0365 нет необходимости проверять следующие две строки, поскольку они уже были оценены. Однако, поскольку итерация уже исправлена, я не могу обойтись без них.

1 ответ
1

Это можно решить с помощью простого groupby операция, которая может сказать вам, как часто появляется каждая комбинация. Тогда вам просто нужно сравнить это с вашим n и отфильтруйте данные:

key = ["begin", "end", "case"]
n = len(systems) // 2

mask = df.groupby(key)["system"].count() > n

df.set_index(key)[mask] 
  .reset_index() 
  .drop(columns="system") 
  .drop_duplicates()

#    begin  end  case
# 0     10   14  0365

Это выводит то же самое, что и ваш код на моей машине, но не то же самое, что и пример вывода, который вы указали в своем вопросе (что я считаю неправильным).

Обратите внимание, что я использовал целочисленное деление // вместо плавающего деления / и вручную переводить в int.

В общем, если вы обнаружите, что используете itertuples в pandas, вам следует очень хорошо подумать, если вы не можете достичь своей цели другим способом, поскольку это один из самых медленных способов сделать что-либо со всеми строками фрейма данных.

  • 1

    Спасибо за подтверждение! То же самое решение осенило меня сегодня утром. (Я исправил пример вывода: я неправильно прочитал для него конечное значение!)

    – horcle_buzz

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *