簡體   English   中英

Pandas.resample 到非整數倍頻

[英]Pandas.resample to a non-integer multiple frequency

我必須將我的數據集從 10 分鍾間隔重新采樣到 15 分鍾間隔,以使其與另一個數據集同步。 根據我在 stackoverflow 上的搜索,我對如何進行有一些想法,但它們都沒有提供干凈清晰的解決方案。

問題

問題設置

#%% Import modules 
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

#%% make timestamps
periods = 12
startdate = '2010-01-01'
timestamp10min = pd.date_range(startdate, freq='10Min', periods=periods)


#%% Make DataFrame and fill it with some data
df = pd.DataFrame(index=timestamp10min)
y = -(np.arange(periods)-periods/2)**2
df['y'] = y 

所需 output

現在我希望已經在 10 分鍾的值保持不變,並且 **:15 和 **:45 的值是 **:10, **:20 和 **:40, * 的平均值*:50。 問題的核心是15分鍾不是10分鍾的倍數。 否則,簡單地應用df.resample('10Min', how='mean')就可以了。

可能的解決方案

  1. 只需使用 15 分鍾的重新采樣,就可以忍受引入的小錯誤。

  2. 使用兩個 forms 的重采樣, close='left', label='left'close='right', label='right' 之后我可以平均兩個重新采樣的 forms。 結果會給我一些結果錯誤,但比第一種方法要小。

  3. 將所有內容重新采樣為 5 分鍾數據,然后應用滾動平均值。 類似的東西在這里被應用: Pandas: rolling mean by time interval

  4. 使用不同數量的輸入重新采樣和平均: 使用 numpy.average 和權重來重新采樣 pandas 數組因此我必須創建一個具有不同權重長度的新系列。 重量是否應在 1 和 2 之間交替。

  5. 將所有內容重新采樣為 5 分鍾數據,然后應用線性插值。 此方法接近方法 3。 Pandas 數據幀:使用線性插值重新采樣編輯:@Paul H 提供了一個可行的解決方案,該解決方案仍然可讀。 謝謝!

所有的方法對我來說都不是很滿意。 有些會導致小錯誤,而其他方法對於局外人來說很難閱讀。

執行

方法 1、2 和 5 的實現以及所需的輸出。 結合可視化。

#%% start plot
plt.figure()
plt.plot(df.index, df['y'], label='original')

#%% resample the data to 15 minutes and plot the result
close = 'left'; label='left'
dfresamplell = pd.DataFrame()
dfresamplell['15min'] = df.y.resample('15Min', how='mean', closed=close, label=label)
labelstring = 'close ' + close + ' label ' + label        
plt.plot(dfresamplell.index, dfresamplell['15min'], label=labelstring)
        
close = 'right'; label='right'
dfresamplerr = pd.DataFrame()
dfresamplerr['15min'] = df.y.resample('15Min', how='mean', closed=close, label=label)
labelstring = 'close ' + close + ' label ' + label        
plt.plot(dfresamplerr.index, dfresamplerr['15min'], label=labelstring)

#%% make an average
dfresampleaverage = pd.DataFrame(index=dfresamplell.index)
dfresampleaverage['15min'] = (dfresamplell['15min'].values+dfresamplerr['15min'].values[:-1])/2
plt.plot(dfresampleaverage.index, dfresampleaverage['15min'], label='average of both resampling methods')

#%% desired output
ydesired = np.zeros(periods/3*2)
i = 0 
j = 0 
k = 0 
for val in ydesired:
    if i+k==len(y): k=0
    ydesired[j] = np.mean([y[i],y[i+k]]) 
    j+=1
    i+=1
    if k==0: k=1; 
    else: k=0; i+=1
plt.plot(dfresamplell.index, ydesired, label='ydesired')


#%% suggestion of Paul H
dfreindex = df.reindex(pd.date_range(startdate, freq='5T', periods=periods*2))
dfreindex.interpolate(inplace=True)
dfreindex = dfreindex.resample('15T', how='first').head()
plt.plot(dfreindex.index, dfreindex['y'], label='method Paul H')


#%% finalize plot
plt.legend()

角度的實現

作為獎勵,我添加了我將用於角度插值的代碼。 這是通過使用復數來完成的。 因為(還)沒有實現復數插值,所以我將復數分成實部和虛部。 平均后這些數字可以再次轉換為天使。 對於某些角度,這是一種比簡單地平均兩個角度更好的重新采樣方法,例如:345 度和 5 度。

#%% make timestamps
periods = 24*6
startdate = '2010-01-01'
timestamp10min = pd.date_range(startdate, freq='10Min', periods=periods)

#%% Make DataFrame and fill it with some data
degrees = np.cumsum(np.random.randn(periods)*25) % 360
df = pd.DataFrame(index=timestamp10min)
df['deg'] = degrees
df['zreal'] = np.cos(df['deg']*np.pi/180)
df['zimag'] = np.sin(df['deg']*np.pi/180)

#%% suggestion of Paul H
dfreindex = df.reindex(pd.date_range(startdate, freq='5T', periods=periods*2))
dfreindex = dfreindex.interpolate()
dfresample = dfreindex.resample('15T', how='first')

#%% convert complex to degrees
def f(x):    
     return np.angle(x[0] + x[1]*1j, deg=True )
dfresample['degrees'] = dfresample[['zreal', 'zimag']].apply(f, axis=1)

#%% set all the values between 0-360 degrees
dfresample.loc[dfresample['degrees']<0] = 360 + dfresample.loc[dfresample['degrees']<0] 

#%% wrong resampling
dfresample['deg'] = dfresample['deg'] % 360

#%% plot different sampling methods
plt.figure()
plt.plot(df.index, df['deg'], label='normal', marker='v')
plt.plot(dfresample.index, dfresample['degrees'], label='resampled according @Paul H', marker='^')
plt.plot(dfresample.index, dfresample['deg'], label='wrong resampling', marker='<')
plt.legend()

我可能誤解了這個問題,但這有用嗎?

TL; DR版本:

import numpy as np
import pandas

data = np.arange(0, 101, 8)
index_10T = pandas.DatetimeIndex(freq='10T', start='2012-01-01 00:00', periods=data.shape[0])
index_05T = pandas.DatetimeIndex(freq='05T', start=index_10T[0], end=index_10T[-1])
index_15T = pandas.DatetimeIndex(freq='15T', start=index_10T[0], end=index_10T[-1])
df1 = pandas.DataFrame(data=data, index=index_10T, columns=['A'])
print(df.reindex(index=index_05T).interpolate().loc[index_15T])

長版

設置假數據

import numpy as np
import pandas

data = np.arange(0, 101, 8)
index_10T = pandas.DatetimeIndex(freq='10T', start='2012-01-01 00:00', periods=data.shape[0])
df1 = pandas.DataFrame(data=data, index=index_10T, columns=['A'])
print(df1)


                      A
2012-01-01 00:00:00   0
2012-01-01 00:10:00   8
2012-01-01 00:20:00  16
2012-01-01 00:30:00  24
2012-01-01 00:40:00  32
2012-01-01 00:50:00  40
2012-01-01 01:00:00  48
2012-01-01 01:10:00  56
2012-01-01 01:20:00  64
2012-01-01 01:30:00  72
2012-01-01 01:40:00  80
2012-01-01 01:50:00  88
2012-01-01 02:00:00  96

然后構建一個新的5分鍾索引並重新索引原始數據幀

index_05T = pandas.DatetimeIndex(freq='05T', start=index_10T[0], end=index_10T[-1])
df2 = df.reindex(index=index_05T)
print(df2)

                      A
2012-01-01 00:00:00   0
2012-01-01 00:05:00 NaN
2012-01-01 00:10:00   8
2012-01-01 00:15:00 NaN
2012-01-01 00:20:00  16
2012-01-01 00:25:00 NaN
2012-01-01 00:30:00  24
2012-01-01 00:35:00 NaN
2012-01-01 00:40:00  32
2012-01-01 00:45:00 NaN
2012-01-01 00:50:00  40
2012-01-01 00:55:00 NaN
2012-01-01 01:00:00  48
2012-01-01 01:05:00 NaN
2012-01-01 01:10:00  56
2012-01-01 01:15:00 NaN
2012-01-01 01:20:00  64
2012-01-01 01:25:00 NaN
2012-01-01 01:30:00  72
2012-01-01 01:35:00 NaN
2012-01-01 01:40:00  80
2012-01-01 01:45:00 NaN
2012-01-01 01:50:00  88
2012-01-01 01:55:00 NaN
2012-01-01 02:00:00  96

然后線性插值

print(df2.interpolate())
                      A
2012-01-01 00:00:00   0
2012-01-01 00:05:00   4
2012-01-01 00:10:00   8
2012-01-01 00:15:00  12
2012-01-01 00:20:00  16
2012-01-01 00:25:00  20
2012-01-01 00:30:00  24
2012-01-01 00:35:00  28
2012-01-01 00:40:00  32
2012-01-01 00:45:00  36
2012-01-01 00:50:00  40
2012-01-01 00:55:00  44
2012-01-01 01:00:00  48
2012-01-01 01:05:00  52
2012-01-01 01:10:00  56
2012-01-01 01:15:00  60
2012-01-01 01:20:00  64
2012-01-01 01:25:00  68
2012-01-01 01:30:00  72
2012-01-01 01:35:00  76
2012-01-01 01:40:00  80
2012-01-01 01:45:00  84
2012-01-01 01:50:00  88
2012-01-01 01:55:00  92
2012-01-01 02:00:00  96

構建一個15分鍾的索引並使用它來提取數據:

index_15T = pandas.DatetimeIndex(freq='15T', start=index_10T[0], end=index_10T[-1])
print(df2.interpolate().loc[index_15T])

                      A
2012-01-01 00:00:00   0
2012-01-01 00:15:00  12
2012-01-01 00:30:00  24
2012-01-01 00:45:00  36
2012-01-01 01:00:00  48
2012-01-01 01:15:00  60
2012-01-01 01:30:00  72
2012-01-01 01:45:00  84
2012-01-01 02:00:00  96

好的,這是一種方法。

  1. 列出您要填寫的時間
  2. 制作包含您想要的時間和已有時間的綜合索引
  3. 獲取您的數據並“向前填充”
  4. 獲取您的數據並“向后填充”
  5. 平均向前和向后填充
  6. 僅選擇所需的行

請注意,這只能起作用,因為您希望值時間上恰好位於您已有的值之間 請注意,最后一次是np.nan因為您沒有任何后續數據。

times_15 = []
current = df.index[0]
while current < df.index[-2]:
    current = current + dt.timedelta(minutes=15)
    times_15.append(current)
combined = set(times_15) | set(df.index)
df = df.reindex(combined).sort_index(axis=0)
df['ff'] = df['y'].fillna(method='ffill')
df['bf'] = df['y'].fillna(method='bfill')
df['solution'] = df[['ff', 'bf']].mean(1)
df.loc[times_15, :]

如果有人完全沒有規律地處理數據,這里有一個改編自上述 Paul H 提供的解決方案。

如果您不想在整個時間序列中進行插值,但僅在重新采樣有意義的地方進行插值,則可以並排保持插值列並以重新采樣和 dropna 結束。

import numpy as np
import pandas

data = np.arange(0, 101, 3)
index_setup = pandas.date_range(freq='01T', start='2022-01-01 00:00',     periods=data.shape[0])
df1 = pandas.DataFrame(data=data, index=index_setup, columns=['A'])
df1 = df1.sample(frac=0.2).sort_index()
print(df1)
                      A
2022-01-01 00:03:00   9
2022-01-01 00:06:00  18
2022-01-01 00:08:00  24
2022-01-01 00:18:00  54
2022-01-01 00:25:00  75
2022-01-01 00:27:00  81
2022-01-01 00:30:00  90

請注意,在沒有任何規律的情況下重新采樣此 DF 會強制將值強制為地板索引,而不進行插值。

print(df1.resample('05T').mean())

                        A
2022-01-01 00:00:00   9.0
2022-01-01 00:05:00  24.0
2022-01-01 00:10:00  39.0
2022-01-01 00:15:00  51.0
2022-01-01 00:20:00   NaN
2022-01-01 00:25:00  79.5

通過在足夠小的間隔內插值然后重新采樣可以實現更好的解決方案。 結果 DF 現在有太多了,但是 dropna() 使它接近其原始形狀。

index_1min = pandas.date_range(freq='01T', start='2022-01-01 00:00', end='2022-01-01 23:59')
df2 = df1.reindex(index=index_1min)
df2['A_interp'] = df2['A'].interpolate(limit_direction='both')
print(df2.resample('05T').first().dropna())

                        A  A_interp
2022-01-01 00:00:00   9.0       9.0
2022-01-01 00:05:00  21.0      15.0
2022-01-01 00:10:00  39.0      30.0
2022-01-01 00:15:00  51.0      45.0
2022-01-01 00:25:00  75.0      75.0

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM