Быстрое усреднение по подмножествам фреймов данных Pandas

#python #pandas

#python #pandas

Вопрос:

Я пытаюсь перебрать большое количество испытаний и вычислить средневзвешенное значение для нескольких подмножеств. В настоящее время данные представлены в формате long со столбцами trial, area score.

   trial  area       score
0  T106     0     0.0035435
1  T106     1     0.0015967
2  T106     4     0.0003191
3  T106     4     0.1272919
4  T288     0     0.1272883
  

У меня около 120 000 испытаний, с 4 областями и, возможно, от 10 до 100 баллов за испытание, в общей сложности ~ 7 миллионов строк. Моей первой мыслью было перебрать все испытания в цикле по 4 областям, создать временный фрейм данных для вычисления результатов и добавления результатов во внешний фрейм данных:

 for area in range(4):
    for trial in trial_names.iloc[:,0]:  
        Tscore = 0
        temp_trial = pd.DataFrame(trials_long.loc[(trials_long['tname'] == trial) amp; (trials_long['area'] == int(area))])
        #match score in tria
        temp_trial = temp_trial.merge(scores_df, how='left')
        #sum score for all matching 'trial'  'area'                      #this will be weigted avrg, with >0.5 *2 and >0.9* 3
        temp_trial.loc[temp_trial['score'] > 0.9, ['score']] *= 3        #weight 3x for  >0.9
        temp_trial.loc[temp_trial['score'] > 0.5, ['score']] *= 2        #weight 2x for >0.5
        Tscore = temp_trial['score'].sum() / int(len(temp_trial.index))
        trial_names.loc[trial,area] = Tscore                    #store Tscore somewhere
        Tscore = 0    
print('done')
  

Это решение занимает более 10 минут в одном потоке с частотой 4,0 ГГц. В этом случае время действительно имеет существенное значение, и вычисления должны выполняться менее чем за 15 секунд или около того. В R я обычно использую ряд векторизованных функций для пропуска циклов, и любые циклы, которые у меня были, будут распараллеливаться по нескольким ядрам, но в python я не знаком с лучшими подходами. Я также был бы открыт для изучения чего-то нового, возможно, хеш-карт?

Спасибо!

Комментарии:

1. на первый взгляд я чувствую, что вы можете просто использовать pandas groupby как для области, так и для пробной версии, затем apply пользовательскую функцию для каждого подмножества, чтобы проверить ваши пороговые значения / рассчитать средневзвешенное значение. это сэкономит вам по крайней мере один из этих циклов for, но, вероятно, оба, если вы сможете векторизовать код внутри функции

2. Возможно, поможет замена нескольких df.loc на udf за один запуск df.apply с помощью. groupby оба столбца — тоже хороший улов!

Ответ №1:

Это то, что я пробовал:

 df['weighted'] = df['score']
df.loc[df['score']>.9, 'weighted'] *= 3        
df.loc[df['score']>.5, 'weighted'] *= 2

# s is indexed by ('trial', 'area')
s = df.groupby(['trial', 'area']).weighted.mean()
  

потребовалось 1,16 секунды, чтобы обработать 7 миллионов строк на 6600 тыс.

Комментарии:

1. Это элегантное и эффективное решение. Спасибо!