Python Pandas: самосоединение для выполнения кумулятивного итога с условиями

#python #pandas

#python #pandas

Вопрос:

Я новичок в Python Pandas и, следовательно, не могу найти синтаксический эквивалент многих обычных операций SQL. Учитывая сценарий игрушки:

 id    rank   ts          alive
1     1      2015-11-01  1
1     2      2015-11-03  1
1     3      2015-11-07  1
2     1      2015-11-03  1
2     2      2015-11-08  1
 

Как бы я добился следующего:

 id    rank   ts          alive   cumulative_age_in_days   mean_id_age_on_this_date
1     1      2015-11-01  1       0                        0
1     2      2015-11-03  1       2                        1
1     3      2015-11-07  1       6                        5
2     1      2015-11-03  1       0                        1
2     2      2015-11-08  1       5                        6
 

где cumulative_day_age — дата текущей строки минус самая ранняя дата для идентификатора. Например, on 2015-11-03 , id=1 составляет 2 дня с момента его первого наблюдения 2015-11-01 . Включено 2015-11-07 , ему 6 дней ( 2015-11-07 2015-11-01 ).

и где mean_id_age_on_this_date средний возраст всего id на дату этой строки, если id имеет alive = 1 . Так что для 2015-11-03 , id=1 2 дня, но id=2 0 дней, так mean_id_age_on_this_date что (0 2)/2 = 1.

Эти два столбца легко сделать в SQL, но мне не хватает знакомства с Python Pandas в той же степени, так что это невероятно сложная задача. Любые подсказки, код или предложения приветствуются.

Ответ №1:

Сначала вычтите первый минимальный день для групп GroupBy.transform с min помощью, а затем выведите timedeltas, преобразуйте в дни Series.dt.days , затем преобразуйте значения, не соответствующие df['alive'].eq(1) ошибочным значениям, Series.where и используйте GroupBy.transform с mean :

 df['ts'] = pd.to_datetime(df['ts'])

df['cumulative_age_in_days'] = df['ts'].sub(df.groupby('id')['ts'].transform('min')).dt.days

df['mean_id_age_on_this_date'] = (df['cumulative_age_in_days'].where(df['alive'].eq(1))
                                                              .groupby(df['ts'])
                                                              .transform('mean'))
print (df)
   id  rank         ts  alive  cumulative_age_in_days  
0   1     1 2015-11-01      1                       0   
1   1     2 2015-11-03      1                       2   
2   1     3 2015-11-07      1                       6   
3   2     1 2015-11-03      1                       0   
4   2     2 2015-11-08      1                       5   

   mean_id_age_on_this_date  
0                         0  
1                         1  
2                         6  
3                         1  
4                         5  
 

Ответ №2:

Используйте pd.to_datetime и Groupby.transform :

 In [1538]: df['ts'] = pd.to_datetime(df['ts']) # Convert `ts` to `datetime`

In [1547]: df['cumulative_age_in_days'] = df.ts.dt.day - df.groupby('id')['ts'].transform('min').dt.day

In [1556]: df['mean_id_age_on_this_date'] = df[df.alive.eq(1)].groupby('ts')['cumulative_age_in_days'].transform('mean')

In [1557]: df
Out[1557]: 
   id  rank         ts  alive  cumulative_age_in_days  mean_id_age_on_this_date
0   1     1 2015-11-01      1                       0                         0
1   1     2 2015-11-03      1                       2                         1
2   1     3 2015-11-07      1                       6                         6
3   2     1 2015-11-03      1                       0                         1
4   2     2 2015-11-08      1                       5                         5
 

Ответ №3:

 def converDate(string):
  return datetime.strptime(string, "%Y-%m-%d")

def getEarly(data):
  min_date = sorted(data["ts"].map(converDate))[0]
  data["cumulative_age_in_days"] = data["ts"].map(lambda el : (converDate(el) - min_date).days)
  return data

def getMean(data): 
  data["mean_id_age_on_this_date"] = int(sum(data["cumulative_age_in_days"]) / len(data))
  return data

data1 = data.groupby("id").apply(getEarly)
data2 = data1.groupby("ts").apply(getMean)
data2