Pandas replace daily observations by monthly mean - pandas

Suppose, I have a pandas Series with daily observations:
pd_series = pd.Series(np.random.rand(26281), index = pd.date_range('2022-01-01', '2024-12-31', freq = 'H'))
pd_series
2022-01-01 00:00:00 0.933746
2022-01-01 01:00:00 0.588907
2022-01-01 02:00:00 0.229040
2022-01-01 03:00:00 0.557752
2022-01-01 04:00:00 0.798649
2024-12-30 20:00:00 0.314143
2024-12-30 21:00:00 0.670485
2024-12-30 22:00:00 0.300531
2024-12-30 23:00:00 0.075403
2024-12-31 00:00:00 0.716685
What I want is to replace every observation by the monthly average. I know that the average can be calculated as
pd_series.resample('MS').mean()
But how do I put the observations to the respective observations?

Use Resampler.transform:
print (pd_series.resample('MS').transform('mean'))
2022-01-01 00:00:00 0.495015
2022-01-01 01:00:00 0.495015
2022-01-01 02:00:00 0.495015
2022-01-01 03:00:00 0.495015
2022-01-01 04:00:00 0.495015
2024-12-30 20:00:00 0.508646
2024-12-30 21:00:00 0.508646
2024-12-30 22:00:00 0.508646
2024-12-30 23:00:00 0.508646
2024-12-31 00:00:00 0.508646
Freq: H, Length: 26281, dtype: float64

Related

Overlap in seconds between datetime range and a time range

I have a dataframe like this:
df11 = pd.DataFrame(
{
"Start_date": ["2018-01-31 12:00:00", "2018-02-28 16:00:00", "2018-02-27 22:00:00"],
"End_date": ["2019-01-31 21:45:00", "2019-03-24 22:00:00", "2018-02-28 01:00:00"],
}
)
Start_date End_date
0 2018-01-31 12:00:00 2019-01-31 21:45:00
1 2018-02-28 16:00:00 2019-03-24 22:00:00
2 2018-02-27 22:00:00 2018-02-28 01:00:00
I need to check the overlap time duration in specific periods in seconds. My expected results are like this:
Start_date End_date 12h-16h 16h-22h 22h-00h 00h-02h30
0 2018-01-31 12:00:00 2019-01-31 21:45:00 14400 20700 0 0
1 2018-02-28 16:00:00 2019-03-24 22:00:00 0 21600 0 0
2 2018-02-27 22:00:00 2018-02-28 01:00:00 0 0 7200 3600
I know it`s completely wrong and I´ve tried other solutions. This is one of my attempts:
df11['12h-16h']=np.where(df11['Start_date']<timedelta(hours=16, minutes=0, seconds=0) & df11['End_date']>timedelta(hours=12, minutes=0, seconds=0),(np.minimum(df11['End_date'],timedelta(hours=16, minutes=0, seconds=0)))-(np.maximum(df11['Start_date'],timedelta(hours=12, minutes=0, seconds=0)))

Shift time series where readings are offset

I want to get readings every 15 minutes starting on the hour given a set of readings that are made hourly but at offset minutes from the hour.
My first approach was to use resample to 15 mins but I did not get expected results:
So if readings are on the hour, resampling works fine:
left_key = pd.to_datetime(['2020-12-01 00:00',
'2020-12-01 01:00',
'2020-12-01 02:00',
'2020-12-01 03:00',
'2020-12-01 04:00',
'2020-12-01 05:00'])
left_data = pd.Series([12,12,13,15,16,15], index=left_key, name='master')
resampled = left_data.resample('15min')
resampled.interpolate(method='spline', order=2)
Yields just what I need:
2020-12-01 00:00:00 12.000000
2020-12-01 00:15:00 11.777455
2020-12-01 00:30:00 12.079464
2020-12-01 00:45:00 12.370313
2020-12-01 01:00:00 12.000000
2020-12-01 01:15:00 12.918527
2020-12-01 01:30:00 13.175893
But if the readings are offset from the hour:
left_key = pd.to_datetime(['2020-12-01 00:06',
'2020-12-01 01:06',
'2020-12-01 02:06',
'2020-12-01 03:06',
'2020-12-01 04:06',
'2020-12-01 05:06'])
left_data = pd.Series([12,12,13,15,16,15], index=left_key, name='master')
resampled = left_data.resample('15min')
resampled.interpolate(method='spline', order=2)
Now I get no data
2020-12-01 00:00:00 NaN
2020-12-01 00:15:00 NaN
2020-12-01 00:30:00 NaN
2020-12-01 00:45:00 NaN
2020-12-01 01:00:00 NaN
And if I resample hourly, it simply shifts the readings back
resampled = left_data.resample('H')
resampled.interpolate(method='spline', order=2)
2020-12-01 00:00:00 12
2020-12-01 01:00:00 12
2020-12-01 02:00:00 13
2020-12-01 03:00:00 15
2020-12-01 04:00:00 16
2020-12-01 05:00:00 15
Is there a way to get resample to interpolate readings so I have the correct value on the hour?
(and is there a better title for this question!)
Update
While the solutions works it is not suitable for larger volumes of data. 1000 rows was too much for my machine! Even reducing the initial resample size required large amounts of memory and time to complete.
Here is another solution from this question: Interpolate one time series onto custom time series
# create a new index for the ranges of datetimes required
starts = df.index.min()
starts = datetime(starts.year, starts.month, starts.day, starts.hour,15*(starts.minute // 15))
master = pd.date_range(starts, df.index.max(), freq="15min")
# will need this to identify original data rows later
df['tag'] = True
# merge with original data and interpolate missing rows
idx = df.index.union(master)
df2 = df.reindex(idx).interpolate('index')
# now remove the things we don't want
df2.drop(df2.index[0], inplace=True) # first value will be NaN (unless has real data)
# use the tag column to remove the original data and then drop that column
df2 = df2[df2['tag'].isna()]
df2.drop(columns=['tag',], inplace=True)
This is much much faster!
OK. This is not the most beautiful of all solutions, but it has worked for me in the past. It's a trick consisting of resampling twice with a negligeable time interval befor applying the one you want. First of all, you need to set your index on time (Dates).
left_key = pd.to_datetime(['2020-12-01 00:06',
'2020-12-01 01:06',
'2020-12-01 02:06',
'2020-12-01 03:06',
'2020-12-01 04:06',
'2020-12-01 05:06'])
left_data = pd.Series([12,12,13,15,16,15])
df = pd.DataFrame({'Dates':left_key , 'Values':left_data})
df.set_index('Dates', inplace=True)
df1 = df.resample('1ms').interpolate(method='spline', order=2).resample('15min').first()
which gives
Values
Dates
2020-12-01 00:00:00 12.000000
2020-12-01 00:15:00 11.653527
2020-12-01 00:30:00 11.960000
2020-12-01 00:45:00 12.255313
2020-12-01 01:00:00 12.539464
2020-12-01 01:15:00 12.812455
2020-12-01 01:30:00 13.074286
2020-12-01 01:45:00 13.324955
2020-12-01 02:00:00 13.564464
2020-12-01 02:15:00 13.792813
2020-12-01 02:30:00 14.010000
2020-12-01 02:45:00 14.216027
2020-12-01 03:00:00 14.410893
2020-12-01 03:15:00 14.594598
2020-12-01 03:30:00 14.767143
2020-12-01 03:45:00 14.928527
2020-12-01 04:00:00 15.078750
2020-12-01 04:15:00 15.217812
2020-12-01 04:30:00 15.345714
2020-12-01 04:45:00 15.462455
2020-12-01 05:00:00 15.568036
Then, you concatenate with your original df
frames = [df, df1]
df2 = pd.concat(frames)
df2.sort_values('Dates')
which returns
Values
Dates
2020-12-01 00:00:00 12.000000
2020-12-01 00:06:00 12.000000
2020-12-01 00:15:00 11.653527
2020-12-01 00:30:00 11.960000
2020-12-01 00:45:00 12.255313
2020-12-01 01:00:00 12.539464
2020-12-01 01:06:00 12.000000
2020-12-01 01:15:00 12.812455
2020-12-01 01:30:00 13.074286
2020-12-01 01:45:00 13.324955
2020-12-01 02:00:00 13.564464
2020-12-01 02:06:00 13.000000
2020-12-01 02:15:00 13.792813
2020-12-01 02:30:00 14.010000
2020-12-01 02:45:00 14.216027
2020-12-01 03:00:00 14.410893
2020-12-01 03:06:00 15.000000
2020-12-01 03:15:00 14.594598
2020-12-01 03:30:00 14.767143
2020-12-01 03:45:00 14.928527
2020-12-01 04:00:00 15.078750
2020-12-01 04:06:00 16.000000
2020-12-01 04:15:00 15.217812
2020-12-01 04:30:00 15.345714
2020-12-01 04:45:00 15.462455
2020-12-01 05:00:00 15.568036
2020-12-01 05:06:00 15.000000

Pandas DateTime Calculating Daily Averages

I have 2 columns of data in a pandas DF that looks like this with the "DateTime" column in format YYYY-MM-DD HH:MM:SS - this is first 24 hrs but the df is for one full year or 8784 x 2.
BAFFIN BAY DateTime
8759 8.112838 2016-01-01 00:00:00
8760 7.977169 2016-01-01 01:00:00
8761 8.420204 2016-01-01 02:00:00
8762 9.515370 2016-01-01 03:00:00
8763 9.222840 2016-01-01 04:00:00
8764 8.872423 2016-01-01 05:00:00
8765 8.776145 2016-01-01 06:00:00
8766 9.030668 2016-01-01 07:00:00
8767 8.394983 2016-01-01 08:00:00
8768 8.092915 2016-01-01 09:00:00
8769 8.946967 2016-01-01 10:00:00
8770 9.620883 2016-01-01 11:00:00
8771 9.535951 2016-01-01 12:00:00
8772 8.861761 2016-01-01 13:00:00
8773 9.077692 2016-01-01 14:00:00
8774 9.116074 2016-01-01 15:00:00
8775 8.724343 2016-01-01 16:00:00
8776 8.916940 2016-01-01 17:00:00
8777 8.920438 2016-01-01 18:00:00
8778 8.926278 2016-01-01 19:00:00
8779 8.817666 2016-01-01 20:00:00
8780 8.704014 2016-01-01 21:00:00
8781 8.496358 2016-01-01 22:00:00
8782 8.434297 2016-01-01 23:00:00
I am trying to calculate daily averages of the "BAFFIN BAY" and I've tried these approaches:
davg_df2 = df2.groupby(pd.Grouper(freq='D', key='DateTime')).mean()
davg_df2 = df2.groupby(pd.Grouper(freq='1D', key='DateTime')).mean()
davg_df2 = df2.groupby(by=df2['DateTime'].dt.date).mean()
All of these approaches yields the same answer as shown below :
BAFFIN BAY
DateTime
2016-01-01 6.008044
However, if you do the math, the correct average for 2016-01-01 is 8.813134 Thank you kindly for your help. I'm assuming the grouping is just by day or 24hrs to make consecutive DAILY averages but the 3 approaches above clearly is looking at other data in my 8784 x 2 DF.
I just ran your df with this code and i get 8.813134:
df['DateTime'] = pd.to_datetime(df['DateTime'])
df = df.groupby(by=pd.Grouper(freq='D', key='DateTime')).mean()
print(df)
Output:
BAFFIN BAY
DateTime
2016-01-01 8.813134

Select data between night and day hours

My data looks like this, it is a minute based data for 2 years.
2017-04-02 00:00:00
2017-04-02 00:01:00
2017-04-02 00:02:00
2017-04-02 00:03:00
2017-04-02 00:04:00
....
2017-04-02 23:59:00
...
2019-02-01 22:54:00
2019-02-01 22:55:00
2019-02-01 22:56:00
2019-02-01 22:57:00
2019-02-01 22:58:00
2019-02-01 22:59:00
2019-02-01 23:00:00
I want to access all the data rows between the end of the workday to the beginning of the next. Example between 2018-04-02 18:00:00 2018-04-03 05:00:00 for all the days in my data frame. Please help
If you use a DatetimeIndex then you can use .between_time
import pandas as pd
df = pd.DataFrame({'date': pd.date_range('2017-04-02', freq='90min', periods=100)})
df = df.set_index('date')
df.between_time('18:00', '5:00')
#date
#2017-04-02 00:00:00
#2017-04-02 01:30:00
#2017-04-02 03:00:00
#2017-04-02 04:30:00
#2017-04-02 18:00:00
#2017-04-02 19:30:00
#2017-04-02 21:00:00
#2017-04-02 22:30:00
#....
One approach is boolean indexing based on conditions on the datetime column or index. Assuming your DataFrame is named df and it has a DatetimeIndex equal to the example data you've posted, try this:
df[(df.index.hour >= 18) | (df.index.hour <= 5)]

Pandas datetime comparison

I have the following dataframe:
start = ['31/12/2011 01:00','31/12/2011 01:00','31/12/2011 01:00','01/01/2013 08:00','31/12/2012 20:00']
end = ['02/01/2013 01:00','02/01/2014 01:00','02/01/2014 01:00','01/01/2013 14:00','01/01/2013 04:00']
df = pd.DataFrame({'start':start,'end':end})
df['start'] = pd.to_datetime(df['start'],format='%d/%m/%Y %H:%M')
df['end'] = pd.to_datetime(df['end'],format='%d/%m/%Y %H:%M')
print(df)
end start
0 2013-01-02 01:00:00 2011-12-31 01:00:00
1 2014-01-02 01:00:00 2011-12-31 01:00:00
2 2014-01-02 01:00:00 2011-12-31 01:00:00
3 2013-01-01 14:00:00 2013-01-01 08:00:00
4 2013-01-01 04:00:00 2012-12-31 20:00:00
I am tying to compare df.end and df.start to two given dates, year_start and year_end:
year_start = pd.to_datetime(2013,format='%Y')
year_end = pd.to_datetime(2013+1,format='%Y')
print(year_start)
print(year_end)
2013-01-01 00:00:00
2014-01-01 00:00:00
But i can't get my comparison to work (comparison in conditions):
conditions = [(df['start'].any()< year_start) and (df['end'].any()> year_end)]
choices = [8760]
df['test'] = np.select(conditions, choices, default=0)
I also tried to define year_end and year_start as follows but it does not work either:
year_start = np.datetime64(pd.to_datetime(2013,format='%Y'))
year_end = np.datetime64(pd.to_datetime(2013+1,format='%Y'))
Any idea on how I could make it work?
Try this:
In [797]: df[(df['start']< year_start) & (df['end']> year_end)]
Out[797]:
end start
1 2014-01-02 01:00:00 2011-12-31 01:00:00
2 2014-01-02 01:00:00 2011-12-31 01:00:00