Come aggiungere righe per un dataframe timeseries?
Sto scrivendo un programma che caricherà in un file excel timeseries in un dataframe, quindi creo diverse nuove colonne utilizzando alcuni calcoli di base. Il mio programma a volte leggerà file excel che mancano mesi per alcuni record. Quindi, nell'esempio seguente, ho i dati sulle vendite mensili per due diversi negozi. I negozi aprono in mesi diversi, quindi la data di fine del primo mese sarà diversa. Ma entrambi dovrebbero avere dati di fine mese fino al 30/9/2020. Nel mio file, Store BBB non ha record per il 31/8/2020 e il 30/9/2020 perché non ci sono state vendite durante quei mesi.
Negozio | Mese aperto | Stato | Città | Data di fine mese | I saldi |
---|---|---|---|---|---|
AAA | 31/5/2020 | NY | New York | 31/5/2020 | 1000 |
AAA | 31/5/2020 | NY | New York | 30/6/2020 | 5000 |
AAA | 31/5/2020 | NY | New York | 30/7/2020 | 3000 |
AAA | 31/5/2020 | NY | New York | 31/8/2020 | 4000 |
AAA | 31/5/2020 | NY | New York | 30/9/2020 | 2000 |
BBB | 30/6/2020 | CT | Hartford | 30/6/2020 | 100 |
BBB | 30/6/2020 | CT | Hartford | 30/7/2020 | 200 |
Quindi, per qualsiasi istanza come questa, voglio essere in grado di aggiungere due righe per Store BBB per 8/31 e 9/30. Le nuove righe devono utilizzare lo stesso mese di apertura, stato e città dalla data di fine mese più recente. Le vendite dovrebbero essere impostate su 0 per entrambe le nuove righe. A partire da ora, eseguo i seguenti passaggi:
- Crea il dataframe "MaxDateData" con il nome del negozio e la data di fine mese massima per ogni negozio e anche la data di fine mese massima per l'intero frame di dati della serie temporale, io chiamo questo campo "Data più recente".
Negozio | Data di fine mese max | Data più recente |
---|---|---|
AAA | 30/9/2020 | 30/9/2020 |
BBB | 30/7/2020 | 30/9/2020 |
- Crea il dataframe "MostRecent" con la riga più recente dal dataframe della serie temporale principale. Per fare ciò, eseguo un join interno tra il dataframe della serie temporale e MaxDateData su Nome negozio e Data di fine mese max.
Negozio | Mese aperto | Stato | Città | Data di fine mese | I saldi | Data di fine mese max | Data più recente |
---|---|---|---|---|---|---|---|
AAA | 31/5/2020 | NY | New York | 30/9/2020 | 2000 | 30/9/2020 | 30/9/2020 |
BBB | 30/6/2020 | CT | Hartford | 30/7/2020 | 200 | 30/7/2020 | 30/9/2020 |
- Creare un dataframe "RequireBackfill_MostRecent" utilizzando una clausola where per filtrare i negozi in cui Max Month End Date <Most Recent Date. Vedere il codice di seguito. Quindi, in questo esempio, la tabella RequireBackfill_MostRecent avrà solo una riga per l'archivio BBB.
RequireBackfill_Stores_MostRecent = MaxDateData.where(MaxDateData['Max Month End Date'] <MaxDateData['Most Recent Date'])
RequireBackfill_MostRecent = MostRecent.merge(RequireBackfill_Stores_MostRecent,how='inner')
- Quindi uso due cicli for nidificati per scorrere le date che devo compilare. Sfrutta il dataframe RequireBackfill_MostRecent che conterrebbe solo Store BBB.
X=[]
end = MaxDateData['Most Recent Date'][0]
for i in MonthlyData['Month End Date'].unique():
per1 = pd.date_range(start = i, end = end, freq ='M')
for val in per1:
Data=[]
Data = RequireBackfill_MostRecent[["Store"
,"Month Opened"
,"City"
,"State"
]].where(RequireBackfill_MostRecent['Max Month End date']==i).dropna()
Data["Month End Date"]= val
Data["Sales"]= 0
X.append(Data)
NewData = pd.concat(X)
- Quindi aggiungo NewData al mio dataframe timeseries usando concat
FullData_List = [MonthlyData,NewData]
FullData=pd.concat(FullData_List)
L'intero processo funziona, ma esiste un modo molto più efficiente per farlo? Questo potrebbe diventare costoso quando comincio a lavorare con dati più grandi.
Risposte
- prova solo
upsample
con l'indice DateTime. ref: pandas-resample-upsample-last-date-edge-of-data
# group by `Store`
# with `Month End Date` column show be converted to DateTime
group.set_index(['Month End Date']).resample('M').asfreq()
- si noti che:
7/30/2020
non è il giorno di fine luglio.7/31/2020
è. quindi l'utilizzo di questo metodo7/30/2020
sarà un problema (converti la data di fine del mese come data di fine effettiva).
Ecco l'approccio passo passo per farlo. Se hai domande, fammi sapere.
import pandas as pd
pd.set_option('display.max_columns', None)
c = ['Store','Month Opened','State','City','Month End Date','Sales']
d = [['AAA','5/31/2020','NY','New York','5/31/2020',1000],
['AAA','5/31/2020','NY','New York','6/30/2020',5000],
['AAA','5/31/2020','NY','New York','7/30/2020',3000],
['AAA','5/31/2020','NY','New York','8/31/2020',4000],
['AAA','5/31/2020','NY','New York','9/30/2020',2000],
['BBB','6/30/2020','CT','Hartford','6/30/2020',100],
['BBB','6/30/2020','CT','Hartford','7/30/2020',200],
['CCC','3/31/2020','NJ','Cranbury','3/31/2020',1500]]
df = pd.DataFrame(d,columns = c)
df['Month Opened'] = pd.to_datetime(df['Month Opened'])
df['Month End Date'] = pd.to_datetime(df['Month End Date'])
#select last entry for each Store
df1 = df.sort_values('Month End Date').drop_duplicates('Store', keep='last').copy()
#delete all rows that have 2020-09-30. We want only ones that are less than 2020-09-30
df1 = df1[df1['Month End Date'] != '2020-09-30']
#set target end date to 2020-09-30
df1['Target_End_Date'] = pd.to_datetime ('2020-09-30')
#calculate how many rows to repeat
df1['repeats'] = df1['Target_End_Date'].dt.to_period('M').astype(int) - df1['Month End Date'].dt.to_period('M').astype(int)
#add 1 month to month end so we can start repeating from here
df1['Month End Date'] = df1['Month End Date'] + pd.DateOffset(months =1)
#set sales value as 0 per requirement
df1['Sales'] = 0
#repeat each row by the value in column repeats
df1 = df1.loc[df1.index.repeat(df1.repeats)].reset_index(drop=True)
#reset repeats to start from 0 thru n using groupby cumcouunt
#this will be used to calculate months to increment from month end date
df1['repeats'] = df1.groupby('Store').cumcount()
#update month end date based on value in repeats
df1['Month End Date'] = df1.apply(lambda x: x['Month End Date'] + pd.DateOffset(months = x['repeats']), axis=1)
#set end date to last day of the month
df1['Month End Date'] = pd.to_datetime(df1['Month End Date']) + pd.offsets.MonthEnd(0)
#drop columns that we don't need anymore. required before we concat dfs
df1.drop(columns=['Target_End_Date','repeats'],inplace=True)
#concat df and df1 to get the final dataframe
df = pd.concat([df, df1], ignore_index=True)
#sort values by Store and Month End Date
df = df.sort_values(by=['Store','Month End Date'],ignore_index=True)
print (df)
L'output di questo è:
Store Month Opened State City Month End Date Sales
0 AAA 2020-05-31 NY New York 2020-05-31 1000
1 AAA 2020-05-31 NY New York 2020-06-30 5000
2 AAA 2020-05-31 NY New York 2020-07-30 3000
3 AAA 2020-05-31 NY New York 2020-08-31 4000
4 AAA 2020-05-31 NY New York 2020-09-30 2000
5 BBB 2020-06-30 CT Hartford 2020-06-30 100
6 BBB 2020-06-30 CT Hartford 2020-07-30 200
7 BBB 2020-06-30 CT Hartford 2020-08-30 0
8 BBB 2020-06-30 CT Hartford 2020-09-30 0
9 CCC 2020-03-31 NJ Cranbury 2020-03-31 1500
10 CCC 2020-03-31 NJ Cranbury 2020-04-30 0
11 CCC 2020-03-31 NJ Cranbury 2020-05-31 0
12 CCC 2020-03-31 NJ Cranbury 2020-06-30 0
13 CCC 2020-03-31 NJ Cranbury 2020-07-31 0
14 CCC 2020-03-31 NJ Cranbury 2020-08-31 0
15 CCC 2020-03-31 NJ Cranbury 2020-09-30 0
Nota ho aggiunto un'altra voce con CCC per mostrarti più variazioni.