時系列データフレームの行を追加するにはどうすればよいですか?

Dec 31 2020

時系列のExcelファイルをデータフレームにロードするプログラムを作成してから、いくつかの基本的な計算を使用していくつかの新しい列を作成します。私のプログラムは、いくつかのレコードで月が欠落しているExcelファイルを読み取ることがあります。したがって、以下の例では、2つの異なる店舗の月間売上データがあります。開店時期が異なるため、最初の月末日が異なります。ただし、どちらも2020年9月30日までの月末データが必要です。私のファイルでは、Store BBBには、2020年8月31日と2020年9月30日のレコードがありません。これは、これらの月に売上がなかったためです。

お店 開業月 状態 月末日 売上高
AAA 2020年5月31日 ニューヨーク ニューヨーク 2020年5月31日 1000
AAA 2020年5月31日 ニューヨーク ニューヨーク 2020年6月30日 5000
AAA 2020年5月31日 ニューヨーク ニューヨーク 2020年7月30日 3000
AAA 2020年5月31日 ニューヨーク ニューヨーク 2020年8月31日 4000
AAA 2020年5月31日 ニューヨーク ニューヨーク 2020年9月30日 2000
BBB 2020年6月30日 CT ハートフォード 2020年6月30日 100
BBB 2020年6月30日 CT ハートフォード 2020年7月30日 200

したがって、このようなインスタンスの場合、8/31と9/30のStoreBBBに2つの行を追加できるようにしたいと思います。新しい行は、直近の月末日から同じ開業月、州、市を使用する必要があります。両方の新しい行の売上を0に設定する必要があります。今のところ、私は次の手順を実行します。

  1. 各ストアのストア名と最大月末日、および時系列データフレーム全体の最大月末日を使用してデータフレーム「MaxDateData」を作成します。このフィールドに「最新の日付」という名前を付けます。
お店 最大月末日 最新の日付
AAA 2020年9月30日 2020年9月30日
BBB 2020年7月30日 2020年9月30日
  1. メインの時系列データフレームからの最新の行を使用してデータフレーム「MostRecent」を作成します。これを行うには、時系列データフレームとストア名および最大月の終了日のMaxDateDataの間で内部結合を行います。
お店 開業月 状態 月末日 売上高 最大月末日 最新の日付
AAA 2020年5月31日 ニューヨーク ニューヨーク 2020年9月30日 2000 2020年9月30日 2020年9月30日
BBB 2020年6月30日 CT ハートフォード 2020年7月30日 200 2020年7月30日 2020年9月30日
  1. where句を使用してデータフレーム「RequireBackfill_MostRecent」を作成し、最大月の終了日<最新の日付であるストアをフィルタリングします。以下のコードを参照してください。したがって、この例では、RequireBackfill_MostRecentテーブルにはストアBBBの行のみが含まれます。
RequireBackfill_Stores_MostRecent = MaxDateData.where(MaxDateData['Max Month End Date'] <MaxDateData['Most Recent Date'])
RequireBackfill_MostRecent = MostRecent.merge(RequireBackfill_Stores_MostRecent,how='inner')
  1. 次に、2つのネストされたforループを使用して、入力する必要のある日付を循環します。これは、StoreBBBのみを含むRequireBackfill_MostRecentデータフレームを利用します。
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) 
  1. 次に、concatを使用してNewDataを時系列データフレームに追加します
FullData_List = [MonthlyData,NewData]
FullData=pd.concat(FullData_List)

このプロセス全体は機能しますが、これを行うためのはるかに効率的な方法はありますか?より大きなデータで作業を開始すると、これはコストがかかる可能性があります。

回答

Ferris Dec 31 2020 at 14:08
  1. upsampleDateTimeインデックスを試してみてください。参照: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()
  1. 注意してください:7/30/2020は7月の終了日ではありません。7/31/2020です。したがって、この方法7/30/2020を使用すると問題が発生します(月末日を実際の終了日に変換します)。
JoeFerndz Dec 31 2020 at 16:14

これを行うためのステップバイステップのアプローチは次のとおりです。ご不明な点がございましたら、お気軽にお問い合わせください。

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)

これの出力は次のとおりです。

   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

より多くのバリエーションを示すために、CCCでもう1つのエントリを追加したことに注意してください。