Je travaille avec des données de journal structurées structurées comme suit (here un extrait pastebin de données simulées pour un bricolage facile ):

import pandas as pd

df = pd.read_csv("https://pastebin.com/raw/qrqTMrGa")
print(df)

     id        date  info_a_cnt  info_b_cnt  has_err
0   123  2020-01-01         123          32        0
1   123  2020-01-02           2          43        0
2   123  2020-01-03          43           4        1
3   123  2020-01-04          43           4        0
4   123  2020-01-05          43           4        0
5   123  2020-01-06          43           4        0
6   123  2020-01-07          43           4        1
7   123  2020-01-08          43           4        0
8   232  2020-01-04          56           4        0
9   232  2020-01-05          97           1        0
10  232  2020-01-06          23          74        0
11  232  2020-01-07          91          85        1
12  232  2020-01-08          91          85        0
13  232  2020-01-09          91          85        0
14  232  2020-01-10          91          85        1

Les variables sont assez simples:

  • id: l'identifiant de la machine observée
  • date: date d'observation
  • info_a_cnt: compte d'un type spécifique d'événement d'information
  • info_b_cnt: comme ci-dessus pour un type d'événement différent
  • has_err: si la machine a enregistré des erreurs ou non

Maintenant, j'aimerais regrouper le dataframe par id pour créer une variable stockant le nombre de jours restants avant un événement d'erreur. Le dataframe souhaité doit ressembler à:

     id        date  info_a_cnt  info_b_cnt  has_err  days_to_err
0   123  2020-01-01         123          32        0            2
1   123  2020-01-02           2          43        0            1
2   123  2020-01-03          43           4        1            0
3   123  2020-01-04          43           4        0            3
4   123  2020-01-05          43           4        0            2
5   123  2020-01-06          43           4        0            1
6   123  2020-01-07          43           4        1            0
7   232  2020-01-04          56           4        0            3
8   232  2020-01-05          97           1        0            2
9   232  2020-01-06          23          74        0            1
10  232  2020-01-07          91          85        1            0
11  232  2020-01-08          91          85        0            2
12  232  2020-01-09          91          85        0            1
13  232  2020-01-10          91          85        1            0

J'ai du mal à trouver la bonne implémentation avec les bonnes fonctions de regroupement.

Éditer:

Toutes les réponses ci-dessous fonctionnent très bien lorsqu'il s'agit de dates avec une granularité quotidienne. Je me demande comment adapter la solution @jezrael ci-dessous à un dataframe contenant des horodatages (les journaux seront regroupés avec un intervalle de 15 minutes):

df:

df = pd.read_csv("https://pastebin.com/raw/YZukAhBz")
print(df)

     id                 date  info_a_cnt  info_b_cnt  has_err
0   123  2020-01-01 12:00:00         123          32        0
1   123  2020-01-01 12:15:00           2          43        0
2   123  2020-01-01 12:30:00          43           4        1
3   123  2020-01-01 12:45:00          43           4        0
4   123  2020-01-01 13:00:00          43           4        0
5   123  2020-01-01 13:15:00          43           4        0
6   123  2020-01-01 13:30:00          43           4        1
7   123  2020-01-01 13:45:00          43           4        0
8   232  2020-01-04 17:00:00          56           4        0
9   232  2020-01-05 17:15:00          97           1        0
10  232  2020-01-06 17:30:00          23          74        0
11  232  2020-01-07 17:45:00          91          85        1
12  232  2020-01-08 18:00:00          91          85        0
13  232  2020-01-09 18:15:00          91          85        0
14  232  2020-01-10 18:30:00          91          85        1

Je me demande comment adapter la réponse @jezrael afin d'atterrir sur quelque chose comme:

     id                 date  info_a_cnt  info_b_cnt  has_err  mins_to_err
0   123  2020-01-01 12:00:00         123          32        0           30
1   123  2020-01-01 12:15:00           2          43        0           15
2   123  2020-01-01 12:30:00          43           4        1            0
3   123  2020-01-01 12:45:00          43           4        0           45
4   123  2020-01-01 13:00:00          43           4        0           30
5   123  2020-01-01 13:15:00          43           4        0           15
6   123  2020-01-01 13:30:00          43           4        1            0
7   123  2020-01-01 13:45:00          43           4        0           60
8   232  2020-01-04 17:00:00          56           4        0           45
9   232  2020-01-05 17:15:00          97           1        0           30
10  232  2020-01-06 17:30:00          23          74        0           15
11  232  2020-01-07 17:45:00          91          85        1            0
12  232  2020-01-08 18:00:00          91          85        0           30
13  232  2020-01-09 18:15:00          91          85        0           15
14  232  2020-01-10 18:30:00          91          85        1            0
1
anddt 29 oct. 2020 à 14:29

2 réponses

Meilleure réponse

Utilisez GroupBy.cumcount avec ascending=False par colonne id et Helper Series avec Series.cumsum mais form back - ainsi ajouté indexation par Series.iloc:

g = f['has_err'].iloc[::-1].cumsum().iloc[::-1]
df['days_to_err'] = df.groupby(['id', g])['has_err'].cumcount(ascending=False)
print(df)
     id        date  info_a_cnt  info_b_cnt  has_err  days_to_err
0   123  2020-01-01         123          32        0            2
1   123  2020-01-02           2          43        0            1
2   123  2020-01-03          43           4        1            0
3   123  2020-01-04          43           4        0            3
4   123  2020-01-05          43           4        0            2
5   123  2020-01-06          43           4        0            1
6   123  2020-01-07          43           4        1            0
7   123  2020-01-08          43           4        0            0
8   232  2020-01-04          56           4        0            3
9   232  2020-01-05          97           1        0            2
10  232  2020-01-06          23          74        0            1
11  232  2020-01-07          91          85        1            0
12  232  2020-01-08          91          85        0            2
13  232  2020-01-09          91          85        0            1
14  232  2020-01-10          91          85        1            0

EDIT: Pour compter la somme cumulée des différences de dates, utilisez la fonction lambda personnalisée avec GroupBy.transform:

df['days_to_err'] = (df.groupby(['id', df['has_err'].iloc[::-1].cumsum()])['date']
                       .transform(lambda x: x.diff().dt.days.cumsum())
                       .fillna(0)
                       .to_numpy()[::-1])
print(df)
     id       date  info_a_cnt  info_b_cnt  has_err  days_to_err
0   123 2020-01-01         123          32        0          2.0
1   123 2020-01-02           2          43        0          1.0
2   123 2020-01-03          43           4        1          0.0
3   123 2020-01-04          43           4        0          3.0
4   123 2020-01-05          43           4        0          2.0
5   123 2020-01-06          43           4        0          1.0
6   123 2020-01-07          43           4        1          0.0
7   123 2020-01-08          43           4        0          0.0
8   232 2020-01-04          56           4        0          3.0
9   232 2020-01-05          97           1        0          2.0
10  232 2020-01-06          23          74        0          1.0
11  232 2020-01-07          91          85        1          0.0
12  232 2020-01-08          91          85        0          2.0
13  232 2020-01-09          91          85        0          1.0
14  232 2020-01-10          91          85        1          0.0

EDIT1: Utilisez Series.dt.total_seconds avec division par 60:

#some data sample cleaning
df = pd.read_csv("https://pastebin.com/raw/YZukAhBz", parse_dates=['date'])
df['date'] = df['date'].apply(lambda x: x.replace(month=1, day=1))
print(df)

df['days_to_err'] = (df.groupby(['id', df['has_err'].iloc[::-1].cumsum()])['date']
                       .transform(lambda x: x.diff().dt.total_seconds().div(60).cumsum())
                       .fillna(0)
                       .to_numpy()[::-1])
print(df)


     id                date  info_a_cnt  info_b_cnt  has_err  days_to_err
0   123 2020-01-01 12:00:00         123          32        0         30.0
1   123 2020-01-01 12:15:00           2          43        0         15.0
2   123 2020-01-01 12:30:00          43           4        1          0.0
3   123 2020-01-01 12:45:00          43           4        0         45.0
4   123 2020-01-01 13:00:00          43           4        0         30.0
5   123 2020-01-01 13:15:00          43           4        0         15.0
6   123 2020-01-01 13:30:00          43           4        1          0.0
7   123 2020-01-01 13:45:00          43           4        0          0.0
8   232 2020-01-01 17:00:00          56           4        0         45.0
9   232 2020-01-01 17:15:00          97           1        0         30.0
10  232 2020-01-01 17:30:00          23          74        0         15.0
11  232 2020-01-01 17:45:00          91          85        1          0.0
12  232 2020-01-01 18:00:00          91          85        0         30.0
13  232 2020-01-01 18:15:00          91          85        0         15.0
14  232 2020-01-01 18:30:00          91          85        1          0.0
2
jezrael 29 oct. 2020 à 13:10

Utilisation:

df2 = df[::-1]
df['days_to_err'] = df2.groupby(['id', df2['has_err'].eq(1).cumsum()]).cumcount()

     id        date  info_a_cnt  info_b_cnt  has_err  days_to_err
0   123  2020-01-01         123          32        0            2
1   123  2020-01-02           2          43        0            1
2   123  2020-01-03          43           4        1            0
3   123  2020-01-04          43           4        0            3
4   123  2020-01-05          43           4        0            2
5   123  2020-01-06          43           4        0            1
6   123  2020-01-07          43           4        1            0
7   123  2020-01-08          43           4        0            0
8   232  2020-01-04          56           4        0            3
9   232  2020-01-05          97           1        0            2
10  232  2020-01-06          23          74        0            1
11  232  2020-01-07          91          85        1            0
12  232  2020-01-08          91          85        0            2
13  232  2020-01-09          91          85        0            1
14  232  2020-01-10          91          85        1            0
0
ansev 29 oct. 2020 à 12:17