Comment ajouter des lignes pour une trame de données de séries temporelles?
J'écris un programme qui se chargera dans un fichier Excel de séries temporelles dans une trame de données, puis je crée plusieurs nouvelles colonnes en utilisant des calculs de base. Mon programme va parfois lire des fichiers Excel qui manquent des mois pour certains enregistrements. Donc, dans l'exemple ci-dessous, j'ai des données de ventes mensuelles pour deux magasins différents. Les magasins ouvrent pendant des mois différents, donc leur première date de fin de mois sera différente. Mais les deux devraient avoir des données de fin de mois jusqu'au 30/09/2020. Dans mon dossier, Store BBB n'a aucun enregistrement pour le 31/08/2020 et le 30/09/2020 car il n'y a pas eu de ventes pendant ces mois.
Magasin | Mois ouvert | État | Ville | Date de fin du mois | Ventes |
---|---|---|---|---|---|
AAA | 31/05/2020 | New York | New York | 31/05/2020 | 1000 |
AAA | 31/05/2020 | New York | New York | 30/06/2020 | 5000 |
AAA | 31/05/2020 | New York | New York | 30/07/2020 | 3000 |
AAA | 31/05/2020 | New York | New York | 31/08/2020 | 4000 |
AAA | 31/05/2020 | New York | New York | 30/09/2020 | 2000 |
BBB | 30/06/2020 | CT | Hartford | 30/06/2020 | 100 |
BBB | 30/06/2020 | CT | Hartford | 30/07/2020 | 200 |
Donc, pour toutes les instances comme celle-ci, je veux pouvoir ajouter deux lignes pour Store BBB pour 8/31 et 9/30. Les nouvelles lignes doivent utiliser les mêmes mois d'ouverture, état et ville à partir de la date de fin de mois la plus récente. Les ventes doivent être définies sur 0 pour les deux nouvelles lignes. À partir de maintenant, je fais les étapes suivantes:
- Créez le Dataframe "MaxDateData" avec le nom du magasin et la date de fin de mois maximum pour chaque magasin ainsi que la date de fin de mois maximum pour l'ensemble de la trame de données de série chronologique, je nomme ce champ "Date la plus récente".
Magasin | Date de fin maximale du mois | Date la plus récente |
---|---|---|
AAA | 30/09/2020 | 30/09/2020 |
BBB | 30/07/2020 | 30/09/2020 |
- Créez la trame de données "MostRecent" avec la ligne la plus récente de la trame de données de la série chronologique principale. Pour ce faire, je fais une jointure interne entre la trame de données de la série chronologique et le MaxDateData sur le nom du magasin et la date de fin de mois maximum.
Magasin | Mois ouvert | État | Ville | Date de fin du mois | Ventes | Date de fin maximale du mois | Date la plus récente |
---|---|---|---|---|---|---|---|
AAA | 31/05/2020 | New York | New York | 30/09/2020 | 2000 | 30/09/2020 | 30/09/2020 |
BBB | 30/06/2020 | CT | Hartford | 30/07/2020 | 200 | 30/07/2020 | 30/09/2020 |
- Créez un dataframe "RequireBackfill_MostRecent" en utilisant une clause where pour filtrer les magasins où la date de fin de mois max <date la plus récente. Voir le code ci-dessous. Ainsi, dans cet exemple, la table RequireBackfill_MostRecent n'aura qu'une ligne pour le magasin BBB.
RequireBackfill_Stores_MostRecent = MaxDateData.where(MaxDateData['Max Month End Date'] <MaxDateData['Most Recent Date'])
RequireBackfill_MostRecent = MostRecent.merge(RequireBackfill_Stores_MostRecent,how='inner')
- J'utilise ensuite deux boucles for imbriquées pour parcourir les dates que je dois remplir. Il exploite le dataframe RequireBackfill_MostRecent qui ne contiendrait que 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)
- J'ajoute ensuite le NewData à ma trame de données de séries temporelles en utilisant concat
FullData_List = [MonthlyData,NewData]
FullData=pd.concat(FullData_List)
Tout ce processus fonctionne, mais y a-t-il un moyen beaucoup plus efficace de le faire? Cela peut devenir coûteux lorsque je commence à travailler avec des données plus volumineuses.
Réponses
- essayez simplement
upsample
l'index 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()
- notez que: ce
7/30/2020
n'est pas la fin de juillet.7/31/2020
est. donc l'utilisation de cette méthode7/30/2020
sera un problème (convertissez la date de fin du mois comme date de fin réelle).
Voici l'approche étape par étape pour ce faire. Si vous avez des questions, faites-le moi savoir.
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)
Le résultat de ceci est:
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
Notez que j'ai ajouté une autre entrée avec CCC pour vous montrer plus de variation.