панды группируются с условием, зависящим от одного и того же столбца

#python #pandas #pandas-groupby

Вопрос:

У меня есть фрейм данных с ID date и number столбцами , и я хотел бы создать новый столбец, который принимает среднее значение всех чисел для этого конкретного ID , НО включает только числа в среднем, где дата меньше даты этой строки. Как бы я это сделал?

 df = (pd.DataFrame({'ID':['1','1','1','1','2','2'],'number':['1','4','1','4','2','5'],  'date':['2021-10-19','2021-10-16','2021-10-16','2021-10-15','2021-10-19','2021-10-10']})  .assign(date = lambda x: pd.to_datetime(x.date))  .assign(mean_no_from_previous_dts = lambda x: x[x.datelt;??].groupby('ID').number.transform('mean'))  )   

это то, что я хотел бы получить в качестве вывода

 ID number date mean_no_from_previous_dts 0 1 1 2021-10-19 3.0 = mean(4 1 4) 1 1 4 2021-10-16 2.5 = mean(4 1) 2 1 1 2021-10-16 4.0 = mean(1) 3 1 4 2021-10-15 0.0 = 0 (as it's the first entry for this date and ID - this number doesnt matter, can e something else) 4 2 2 2021-10-19 5.0 = mean(5) 5 2 5 2021-10-10 0.0 = 0 (as it's the first entry for this date and ID)  

так, например, первая запись столбца mean_no_from_previous_dts -это среднее значение (4 1 4) : первое 4 происходит из столбца number и 2-й строки, потому что 2021-10-16 (дата во 2-й строке) меньше, чем 2021-10-19 (дата в 1-й строке). Это 1 происходит из 3-го ряда, потому что 2021-10-16 меньше, чем 2021-10-19. Второй 4 идет из 4-го ряда, потому что 2021-10-15 меньше, чем 2021-10-19. Это для ID = 1 того же самого, для ID = 2

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

1. where date is smaller than the current date. таким образом, это означает, что все ценности включены, потому что сегодня есть 2021-10-20 ?

2. нет, я имею в виду дату текущей строки. Итак, для первой строки: 2021-10-16, 2021-10-16 и 2021-10-15 меньше, чем 2021-10-19, поэтому возьмите среднее значение: (4 1 4)/3

3. Эй, я не понимаю — что такое столбец no_of_previous_dts?

4. Я попытался отредактировать его и сделать более понятным.

Ответ №1:

Вот решение с широковещательной передачей numpy для групп:

 df = (pd.DataFrame({'ID':['1','1','1','1','2','2'],'number':['1','4','1','4','2','5'],  'date':['2021-10-19','2021-10-16','2021-10-16','2021-10-15','2021-10-19','2021-10-10']})  .assign(date = lambda x: pd.to_datetime(x.date), number = lambda x: x['number'].astype(int))  )  

 def f(x):  arr = x['date'].to_numpy()  m = arr lt;= arr[:, None]  #remove rows with same values - set mask to False  np.fill_diagonal(m, False)  #set greater values to `NaN` and get mean without NaNs  m = np.nanmean(np.where(m, x['number'].to_numpy(), np.nan).astype(float), axis=1)  #assign to new column  x['no_of_previous_dts'] = m  return x  #last value is set to 0 per groups df = df.groupby('ID').apply(f).fillna({'no_of_previous_dts':0})   print (df)  ID number date no_of_previous_dts 0 1 1 2021-10-19 3.0 1 1 4 2021-10-16 2.5 2 1 1 2021-10-16 4.0 3 1 4 2021-10-15 0.0 4 2 2 2021-10-19 5.0 5 2 5 2021-10-10 0.0  

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

1. большое спасибо. Это очень медленно с большим кадром данных. Неужели для этого нет метода панд? Может быть, что-то с роллингом или рангом?

2. @corianne1234 — Если использовать df['g'] = df.groupby(['ID','date']).cumcount() , и df1 = df.pivot('ID', ['date', 'g'], 'number').sort_index(axis=1) что тогда print (df1.shape) ?