Implementación vectorizada para crear múltiples filas a partir de una sola fila en el dataframe de pandas

Para cada fila en la tabla de entrada, necesito generar múltiples filas al separar el rango de fechas en base mensual. (Por favor, consulte la salida de muestra a continuación).

Existe un enfoque iterativo simple para convertir fila por fila, pero es muy lento en grandes marcos de datos.

¿Alguien podría sugerir un enfoque vectorizado, como el uso de apply (), map () etc. para lograr el objective?

La tabla de salida es una tabla nueva.

Entrada:

    ID, START_DATE, END_DATE 1, 2010-12-08, 2011-03-01 2, 2010-12-10, 2011-01-12 3, 2010-12-16, 2011-03-07 

    Salida:

     ID, START_DATE, END_DATE, NUMBER_DAYS, ACTION_DATE 1, 2010-12-08, 2010-12-31, 23, 201012 1, 2010-12-08, 2011-01-31, 54, 201101 1, 2010-12-08, 2011-02-28, 82, 201102 1, 2010-12-08, 2011-03-01, 83, 201103 2, 2010-12-10, 2010-12-31, 21, 201012 2, 2010-12-10, 2011-01-12, 33, 201101 3, 2010-12-16, 2010-12-31, 15, 201012 4, 2010-12-16, 2011-01-31, 46, 201101 5, 2010-12-16, 2011-02-28, 74, 201102 6, 2010-12-16, 2011-03-07, 81, 201103 

    Creo que puedes usar:

     import pandas as pd df = pd.DataFrame({'ID': {0: 1, 1: 2, 2: 3}, 'END_DATE': {0: pd.Timestamp('2011-03-01 00:00:00'), 1: pd.Timestamp('2011-01-12 00:00:00'), 2: pd.Timestamp('2011-03-07 00:00:00')}, 'START_DATE': {0: pd.Timestamp('2010-12-08 00:00:00'), 1: pd.Timestamp('2010-12-10 00:00:00'), 2: pd.Timestamp('2010-12-16 00:00:00')}}, columns=['ID','START_DATE', 'END_DATE']) print df ID START_DATE END_DATE 0 1 2010-12-08 2011-03-01 1 2 2010-12-10 2011-01-12 2 3 2010-12-16 2011-03-07 

     #if multiple columns, you can filter them by subset #df = df[['ID','START_DATE', 'END_DATE']] #stack columns START_DATE and END_DATE df1 = df.set_index('ID') .stack() .reset_index(level=1, drop=True) .to_frame() .rename(columns={0:'Date'}) #print df1 #resample and fill missing data df1 = df1.groupby(df1.index).apply(lambda x: x.set_index('Date').resample('M').asfreq()) .reset_index() print df1 ID Date 0 1 2010-12-31 1 1 2011-01-31 2 1 2011-02-28 3 1 2011-03-31 4 2 2010-12-31 5 2 2011-01-31 6 3 2010-12-31 7 3 2011-01-31 8 3 2011-02-28 9 3 2011-03-31 

    Hay un problema con el último día del Month , porque la resample agrega el último día del Month , así que primero cree las columnas de period y luego merge . Mediante combine_first agregue los valores perdidos de la columna Date y bfill agregue los valores perdidos de la columna START_DATE .

     df['period'] = df.END_DATE.dt.to_period('M') df1['period'] = df1.Date.dt.to_period('M') df2 = pd.merge(df1, df, on=['ID','period'], how='left') df2['END_DATE'] = df2.END_DATE.combine_first(df2.Date) df2['START_DATE'] = df2.START_DATE.bfill() df2 = df2.drop(['Date','period'], axis=1) 

    Por último, agregue nuevas columnas por diferencia con dt.days y dt.strftime :

     df2['NUMBER_DAYS'] = (df2.END_DATE - df2.START_DATE).dt.days df2['ACTION_DATE'] = df2.END_DATE.dt.strftime('%Y%m') print df2 ID START_DATE END_DATE NUMBER_DAYS ACTION_DATE 0 1 2010-12-08 2010-12-31 23 201012 1 1 2010-12-08 2011-01-31 54 201101 2 1 2010-12-08 2011-02-28 82 201102 3 1 2010-12-08 2011-03-01 83 201103 4 2 2010-12-10 2010-12-31 21 201012 5 2 2010-12-10 2011-01-12 33 201101 6 3 2010-12-16 2010-12-31 15 201012 7 3 2010-12-16 2011-01-31 46 201101 8 3 2010-12-16 2011-02-28 74 201102 9 3 2010-12-16 2011-03-07 81 201103 

    También puedes probar esto. Usando la función Pandas date_range y DataFrame aplique el concepto.

    En su Ouptut, para la ID después de 3, ha mencionado 4,5,6. Creo que debería ser 3. Por favor, compruebe.

     import pandas as pd from datetime import datetime l_ret_df = pd.DataFrame(columns=('ID', 'START_DATE', 'END_DATE', 'NUMBER_DAYS', 'ACTION_DATE')) def generate_ts_df(p_row): l_id = p_row['ID'] l_start = p_row['START_DATE'] l_start_date = datetime.strptime(l_start,'%Y-%m-%d') l_end = p_row['END_DATE'] l_end_date = datetime.strptime(l_end,'%Y-%m-%d') l_df = pd.date_range(start=l_start,end=l_end,freq='M',closed=None) global l_ret_df for e in l_df: l_ret_df = l_ret_df.append(pd.DataFrame([[l_id,l_start,e.date(),(e.date()-l_start_date.date()).days,e.strftime('%Y%m')]],columns=('ID', 'START_DATE', 'END_DATE', 'NUMBER_DAYS', 'ACTION_DATE'))) l_ret_df = l_ret_df.append(pd.DataFrame([[l_id,l_start,l_end,(l_end_date.date()-l_start_date.date()).days,l_end_date.strftime('%Y%m')]],columns=('ID', 'START_DATE', 'END_DATE', 'NUMBER_DAYS', 'ACTION_DATE'))) return 1 if __name__ == "__main__": l_ts_base = pd.DataFrame([[1, '2010-12-08', '2011-03-01'], [2, '2010-12-10', '2011-01-12'], [3, '2010-12-16', '2011-03-07']], columns=('ID', 'START_DATE', 'END_DATE')) l_ts_base.apply(generate_ts_df, axis=1) print l_ret_df 

    Salida

      ID START_DATE END_DATE NUMBER_DAYS ACTION_DATE 0 1 2010-12-08 2010-12-31 23 201012 0 1 2010-12-08 2011-01-31 54 201101 0 1 2010-12-08 2011-02-28 82 201102 0 1 2010-12-08 2011-03-01 83 201103 0 2 2010-12-10 2010-12-31 21 201012 0 2 2010-12-10 2011-01-12 33 201101 0 3 2010-12-16 2010-12-31 15 201012 0 3 2010-12-16 2011-01-31 46 201101 0 3 2010-12-16 2011-02-28 74 201102 0 3 2010-12-16 2011-03-07 81 201103