Выровняйте разные диапазоны дат в «последние x дней» для каждой строки - PullRequest
0 голосов
/ 13 сентября 2018

Мне нужно проанализировать последние 60 дней до последней даты, когда каждый пользователь был активным.

Мой фрейм данных содержит даты ('CalendarDate'), когда каждый пользователь ('DataSourceId') был активным ('Activity'целое число) - одна строка на дату.Я сгруппировал фрейм данных по DataSourceId, поэтому у меня есть даты в столбцах, и я взял последний день, когда каждый пользователь был активен 'max_date':

df['max_date'] = df.groupby('DataSourceId')['CalendarDate'].transform('max')

Данные выглядят примерно так, хотя 'CalendarDate' и 'max_date 'на самом деле datetime64[ns] формат (значения Activity float64):

ID    Jan1    Jan2    Jan3    Jan4    Jan5...  max_date
1               8              15      10        Jan5
2       2              13                        Jan3
3       6      11                                Jan2

Теперь я хочу перестроить столбцы из календарных дат в "последние x дней" для каждой строки.Например:

ID    Last    Last-1    Last-2    Last-3  ...  Last-x
1      10       15                   8  
2      13                  2           
3      11        6

Мне не удалось найти никаких примеров подобных преобразований, и я действительно застрял здесь.

РЕДАКТИРОВАНИЕ: После адаптации решения Jezrael я заметилиногда это не удавалось.

Я думаю, что проблема связана с этим кодом в решении jezrael: r = data_wide.bfill().isna().sum(axis=1).values

Пример: эти данные не выполняются (и r = [0 3]):

CalendarDate                         2017-07-02 2017-07-03 2017-07-06 2017-07-07 2017-07-08 2017-07-09
DataSourceId                                                                                          
1000648                                     NaN     188.37     178.37        NaN     128.37      18.37
1004507                                   51.19        NaN      52.19      53.19        NaN        NaN

В частности, перестроенный фрейм данных выглядит следующим образом:

              Last-0  Last-1  Last-2  Last-3  Last-4  Last-5
DataSourceId                                                
1000648        18.37  128.37     NaN  178.37  188.37     NaN
1004507        52.19     NaN   51.19     NaN     NaN   53.19

Если я изменяю порядок в фрейме данных, меняя идентификатор 1000648 на 1100648 (чтобы он стал второй строкой), это результат (r = [0 2]):

              Last-0  Last-1  Last-2  Last-3  Last-4  Last-5
DataSourceId                                                
1004507          NaN     NaN   53.19   52.19     NaN   51.19
1100648          NaN  178.37  188.37     NaN   18.37  128.37

Ответы [ 3 ]

0 голосов
/ 13 сентября 2018

Вы можете использовать этот код, сначала находя последние непрерывные нулевые значения, и при подсчете смещения для каждой серии он будет работать.

df1 = df[df.columns.difference(['ID'])]
df1 = df1.apply(lambda x:x.shift(x[::-1].isnull().cumprod().sum())[::-1],axis=1)
df1.columns = ['Last-'+str(i) for i in range(df1.columns.shape[0])]
df1['ID'] = df['ID']

Out:

   Last-0   Last-1  Last-2  Last-3  Last-4  ID
0   10.0    15.0    NaN     8.0     NaN     1
1   13.0    NaN     2.0     NaN     NaN     2
2   11.0    6.0     NaN     NaN     NaN     3
0 голосов
/ 13 сентября 2018

Если важна производительность, используйте немного измененный numpy solution:

#select all columns without last
A = df.iloc[:, 1:-1].values
print (A)
[[nan  8. nan 15. 10.]
 [ 2. nan 13. nan nan]
 [ 6. 11. nan nan nan]]

#count NaNs values
r = df.bfill(axis=1).isna().sum(axis=1).values
#oldier pandas versions
#r = df.bfill(axis=1).isnull().sum(axis=1).values
#boost solution by https://stackoverflow.com/a/30428192
#r = A.shape[1] - (~np.isnan(A)).cumsum(axis=1).argmax(axis=1) - 1
print (r)
[0 2 3]

rows, column_indices = np.ogrid[:A.shape[0], :A.shape[1]]

# Use always a negative shift, so that column_indices are valid.
# (could also use module operation)
r[r < 0] += A.shape[1]
column_indices = np.flip(column_indices - r[:,np.newaxis], axis=1)
print (column_indices)
[[ 4  3  2  1  0]
 [ 2  1  0 -1 -2]
 [ 1  0 -1 -2 -3]]

result = A[rows, column_indices]
#https://stackoverflow.com/a/51613442
#result = strided_indexing_roll(A,r)
print (result)
[[10. 15. nan  8. nan]
 [13. nan  2. nan nan]
 [11.  6. nan nan nan]]

c = [f'Last-{x}' for x in np.arange(result.shape[1])]
df1 = pd.DataFrame(result, columns=c)
df1.insert(0, 'ID', df['ID'])
print (df1)
   ID  Last-0  Last-1  Last-2  Last-3  Last-4
0   1    10.0    15.0     NaN     8.0     NaN
1   2    13.0     NaN     2.0     NaN     NaN
2   3    11.0     6.0     NaN     NaN     NaN

РЕДАКТИРОВАТЬ:

Если IDИндекс, затем решение немного изменено - не удаляйте первый столбец с помощью .iloc[:, :-1] и только последний использованный DataFrame конструктор:

A = df.iloc[:, :-1].values
print (A)
[[nan  8. nan 15. 10.]
 [ 2. nan 13. nan nan]
 [ 6. 11. nan nan nan]]

r = df.bfill(axis=1).isna().sum(axis=1).values
print (r)
[0 2 3]

rows, column_indices = np.ogrid[:A.shape[0], :A.shape[1]]

# Use always a negative shift, so that column_indices are valid.
# (could also use module operation)
r[r < 0] += A.shape[1]
column_indices = np.flip(column_indices - r[:,np.newaxis], axis=1)
print (column_indices)
[[ 4  3  2  1  0]
 [ 2  1  0 -1 -2]
 [ 1  0 -1 -2 -3]]

result = A[rows, column_indices]
print (result)
[[10. 15. nan  8. nan]
 [13. nan  2. nan nan]
 [11.  6. nan nan nan]]

c = [f'Last-{x}' for x in np.arange(result.shape[1])]
#use DataFrame constructor
df1 = pd.DataFrame(result, columns=c, index=df.index)
print (df1)
    Last-0  Last-1  Last-2  Last-3  Last-4
ID                                        
1     10.0    15.0     NaN     8.0     NaN
2     13.0     NaN     2.0     NaN     NaN
3     11.0     6.0     NaN     NaN     NaN
0 голосов
/ 13 сентября 2018

Пожалуйста, попробуйте следующий код и дайте мне знать, если это поможет.

df = df.iloc[:,list(range(len(df.columns)-1,0,-1))]
print(df)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...