Условное объединение фреймов данных по временной переменной - PullRequest
0 голосов
/ 28 мая 2020

Мне нужен фрейм данных, который я хочу объединить по переменной даты, идентификатора и времени, чтобы вычислить продолжительность. в df2 необходимо go с ближайшим предыдущим временем в df1.

Промежуточный результат будет

intermediateResult = DataFrame({
'id': ['a']*8,
'date': ['02-02-2015']*8,
'time_1': ['08:00:00', '08:00:00', '08:00:00','08:00:00', '09:00:00', '10:30:00', '10:30:00', '12:45'],
'time_2': ['08:00:00', '08:09:00', '08:04:01','08:52:36', '09:34:25', '10:30:00', '11:23:38', nan] })

intermediateResult
    id  date        time_1      time_2
0   a   02-02-2015  08:00:00    08:00:00
1   a   02-02-2015  08:00:00    08:09:00
2   a   02-02-2015  08:00:00    08:04:01
3   a   02-02-2015  08:00:00    08:52:36   # end
4   a   02-02-2015  09:00:00    09:34:25   # end
5   a   02-02-2015  10:30:00    10:30:00
6   a   02-02-2015  10:30:00    11:23:38   # end
7   a   02-02-2015  12:45           NaN

Наконец, я хочу получить разницу во времени между последним временем time_2 каждого период (обозначается комментарием # end) и соответствующее им time_1.

Окончательный результат будет выглядеть так

finalResult = DataFrame({
'id': ['a']*4,
'date': ['02-02-2015']*4,
'Duration': ['00:52:36', '00:34:25', '00:53:38', nan]})

finalResult 
    id  date        Duration
0   a   02-02-2015  00:52:36
1   a   02-02-2015  00:34:25
2   a   02-02-2015  00:53:38
3   a   02-02-2015  NaN

Ответы [ 2 ]

1 голос
/ 29 мая 2020

Используя разные методы слияния, пришел к одному и тому же ответу. В итоге использовал merge_as0f direction =backward согласно вашему запросу. К сожалению, не похож на ваш в том смысле, что у меня нет NaN. Рад помочь дальше, если вы предоставили информацию о том, как вы получаете NaN в одной строке.

    #Join dateto time and coerce to datetime
    df1['datetime']=pd.to_datetime(df1.date.str.cat(df1.time_1,sep=' '))
    df2['datetime']=pd.to_datetime(df2.date.str.cat(df2.time_2,sep=' '))
    df2['time_2'] = df2['time_2'].apply(lambda x: (x[-5:]))#StripHours from time_2. I anticipate to use it as duration
    #sort to allow merge_asof
    df1=df1.sort_values('datetime')
    df2=df2.sort_values('datetime')
    #Merge to the dataframes joining using datetime to the nearest hour
    df3=pd.merge_asof(df2, df1,on='datetime', by='id', tolerance=pd.Timedelta('2H'),allow_exact_matches=True,direction='backward').dropna()
    #df3=df2.merge(df1, left_on=df2.datetime.dt.hour, right_on=df1.datetime.dt.hour, how='left').drop(columns=['key_0', 'id_y', 'date_y']).fillna(0)#Alternative merge
    df3.set_index('datetime', inplace=True)#set datetime as index
    df3['minutes']=df3.index.minute#Extract minute in each row. Looks to me you want the highest minute in each hour
    #Groupby hour idxmax Helps boolean select the index with the highest minutes in an hour. aND DROP UNWANTED ROWS
    finalResult=df3.loc[df3.groupby([df3.index.hour, df3.date_x])['minutes'].idxmax()].reset_index().drop(columns=['datetime','time_1','date_y','minutes'])
   finalResult.columns=['id','date','Duration(min)']

finalResult

enter image description here

0 голосов
/ 29 мая 2020

Используя решение, предложенное @wwnde, я нашел тот, который лучше масштабируется для моего реального набора данных:

import numpy as np
import pandas as pd

df1 = DataFrame({
    'id': ['a']*4,
    'date': ['02-02-2015']*4,
    'time_1': ['08:00:00', '09:00:00', '10:30:00', '12:45:00']
})

df2 = DataFrame({
    'id': ['a']*7,
    'date': ['02-02-2015',
        '02-02-2015',
        '03-02-2015', # small change here relatively to the df un my first post
        '02-02-2015',
        '02-02-2015',
        '02-02-2015',
        '02-02-2015'],
    'time_2': ['08:00:00', '08:09:00', '08:04:01','08:52:36', '09:34:25', '10:30:00', '11:23:38']
})


----------------------------------------------------

def preproDf(df1, df2, time_1, time_2, _id, date):
    '''
    Preprocess the dataframes for the following operations

    df1: pd.DataFrame, left dataframe
    df2: pd.DataFrame, right dataframe
    time_1:str, name of the left dataframe
    time_2:str, name of the right dataframe
    _id:str, name of the id variable. Should be the same for both dataframes
    date:str, name of the date variable. Should be the same for both dataframes

    return: None
    '''
    df2[time_2] = df2[time_2].apply(pd.to_datetime)
    df1[time_1] = df1[time_1].apply(pd.to_datetime)

    #sort to allow merge_asof
    df1=df1.sort_values([_id, date, time_1])
    df2=df2.sort_values([_id, date, time_2])



def processDF(df1, df2, time_1, time_2, _id, date):
    # initialisation
    groupKeys = list(df2.groupby([_id, date]).groups.keys())
    dfGroup=groupKeys[0]
    group = df2.groupby([_id, date]).get_group(dfGroup)
    rslt = pd.merge_asof(group, df1, left_on=time_2,  right_on=time_1, by=[_id, date], tolerance=pd.Timedelta('2H'),allow_exact_matches=True,direction='backward')#.dropna()

    # For loop to get the values in an array
    for group in groupKeys[1:]: # Iteration start at the second elmt
        group = df2.groupby([_id, date]).get_group(group)
        item = pd.merge_asof(group, df1, left_on=time_2,  right_on=time_1, by=[_id, date], tolerance=pd.Timedelta('2H'),allow_exact_matches=True,direction='backward')#.dropna()
        rslt = np.vstack((rslt, item))
    rslt = DataFrame(rslt, columns=item.columns)

    # Creating timeDifference variable
    rslt['timeDifference'] = rslt[time_2] - rslt[time_1]

    # Getting the actual result
    rslt = rslt.groupby([_id, date, time_1]).timeDifference.max()
    rslt = pd.DataFrame(rslt).reset_index()
    rslt.rename({time_1: 'openTime'}, axis='columns')

    return rslt

Результат:

preproDf(df1, df2, 'time_1', 'time_2', 'id', 'date')

processDF(df1, df2, 'time_1', 'time_2', 'id', 'date')
    id  date    time_1  screenOnDuration
0   a   02-02-2015  2020-05-29 08:00:00 00:52:36
1   a   02-02-2015  2020-05-29 09:00:00 00:34:25
2   a   02-02-2015  2020-05-29 10:30:00 00:53:38
...