Pandas: Set first 2 hours of every group to NaN - python

I am trying to clean my data by setting 'value' to NaN for the first 2 hours of every 'state' group.
My dataframe looks like this:
>>> import pandas as pd
>>> import numpy as np
>>>
>>> rng = pd.date_range('1/1/2016', periods=6, freq='H')
>>>
>>> data = {'value': np.random.rand(len(rng)),
... 'state': ['State 1']*3 + ['State 2']*3}
>>> df = pd.DataFrame(data, index=rng)
>>>
>>> df
state value
2016-01-01 00:00:00 State 1 0.800798
2016-01-01 01:00:00 State 1 0.130290
2016-01-01 02:00:00 State 1 0.464372
2016-01-01 03:00:00 State 2 0.925445
2016-01-01 04:00:00 State 2 0.732331
2016-01-01 05:00:00 State 2 0.811541
I've come up with three ways of doing this, and both don't work:
1) First attempt using .loc and/or .ix result in no change:
>>> df.loc[df.state=='State 2'].first('2H').value = np.nan
>>> df.ix[df.state=='State 2'].first('2H').value = np.nan
>>> df
state value
2016-01-01 00:00:00 State 1 0.800798
2016-01-01 01:00:00 State 1 0.130290
2016-01-01 02:00:00 State 1 0.464372
2016-01-01 03:00:00 State 2 0.925445
2016-01-01 04:00:00 State 2 0.732331
2016-01-01 05:00:00 State 2 0.811541
2) Second attempt results in an error:
>>> df.loc[df.state=='State 2', 'value'].first('2H') = np.nan
File "<stdin>", line 1
SyntaxError: can't assign to function call
3) This is a hackish attempt that worked, but is apparently discouraged:
>>> temp = df.loc[df.state=='State 2']
>>> temp.first('2H').value = np.nan
/home/user/anaconda3/lib/python3.5/site-packages/pandas/core/generic.py:2698: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
self[name] = value
>>> df.loc[df.state=='State 2'] = temp
>>> df
state value
2016-01-01 00:00:00 State 1 0.800798
2016-01-01 01:00:00 State 1 0.130290
2016-01-01 02:00:00 State 1 0.464372
2016-01-01 03:00:00 State 2 NaN
2016-01-01 04:00:00 State 2 NaN
2016-01-01 05:00:00 State 2 0.811541
Ideally, I want to determine an easy way to loop over each group and clean the beginning and end of their respective data groups. I was under the impression that .first and .last would be great due to their simple time string formats.
Using .loc doesn't take into account these time string formats, but I'm probably missing something.
What's the true way of doing this in pandas?

Find all indexes by first 2H, then change index to Multiindex, swaplevel for matching ix and last reset_index:
idx = df.groupby('state')['value'].apply(lambda x: x.first('2H')).index
df.set_index('state', append=True, inplace=True)
df = df.swaplevel(0,1)
df.ix[idx,'value'] = np.nan
print (df.reset_index(level=0))
state value
2016-01-01 00:00:00 State 1 NaN
2016-01-01 01:00:00 State 1 NaN
2016-01-01 02:00:00 State 1 0.406512
2016-01-01 03:00:00 State 2 NaN
2016-01-01 04:00:00 State 2 NaN
2016-01-01 05:00:00 State 2 0.226350

Related

Finding skipped index time steps and filling the values in a Pandas DataFrame

I am working with a pandas DataFrame that has an index that skips one or more time steps which in my case is one or more hours. I want to know if there is a way to find these time step skips and possibly insert these missing time steps.
Example of what I have:
[In]: df
[Out]:
point_value
Timestamp
2016-01-01 00:00:00 2550.63
2016-01-01 01:00:00 2535.97
2016-01-01 02:00:00 2538.25
2016-01-01 04:00:00 2548.63
2016-01-01 05:00:00 2555.16
Example of what I am looking for:
[In]: df
[Out]:
point_value
Timestamp
2016-01-01 02:00:00 2538.25
2016-01-01 04:00:00 2548.63
Ideally after finding these time step gaps I'd want to fill them with the time steps missing as such:
[In]: df
[Out]:
point_value
Timestamp
2016-01-01 00:00:00 2550.63
2016-01-01 01:00:00 2535.97
2016-01-01 02:00:00 2538.25
2016-01-01 03:00:00 NaN
2016-01-01 04:00:00 2548.63
2016-01-01 05:00:00 2555.16
I have searched on stack overflow and can't seem to find something that pertains to the index itself. If this is a duplicated question then I will be happy to take it down and find the result. Thanks for the help in advance.
DataFrame.reindex should achieve what you are looking for. Just define a new index and apply it to your dataframe:
new_index = pd.date_range(start='1/1/2016 0:0:0', end='1/1/2016 5:0:0', periods=6)
df.reindex(index=new_index)
With exactly hourly timestamps you can use resample
df.resample('H').first()
point_value
Timestamp
2016-01-01 00:00:00 2550.63
2016-01-01 01:00:00 2535.97
2016-01-01 02:00:00 2538.25
2016-01-01 03:00:00 NaN
2016-01-01 04:00:00 2548.63
2016-01-01 05:00:00 2555.16

grouping multiple time values into a start and finish time

I have a dataframe as follows
import pandas as pd
import numpy as np
IDs = ['A','A','A','B','B']
times = pd.date_range(start='01/01/2019',end='01/02/2019',freq='h')
times_2 = pd.date_range(start='01/01/2019',end='01/02/2019',freq='h') + pd.Timedelta('15min')
Vals = [np.random.randint(15,250) for x in enumerate(times)]
df = pd.DataFrame({'id' : IDs*5,
'Start' : times,
'End' : times_2,
'Value' : Vals},columns=['id','Start','End','Value'])
this gives me a df as follows.
print(df.head(5))
id Start End Value
0 A 2019-01-01 00:00:00 2019-01-01 00:15:00 52
1 A 2019-01-01 01:00:00 2019-01-01 01:15:00 69
2 A 2019-01-01 02:00:00 2019-01-01 02:15:00 209
3 B 2019-01-01 03:00:00 2019-01-01 03:15:00 163
4 B 2019-01-01 04:00:00 2019-01-01 04:15:00 70
now what I'm trying to do is apply a group by to my data frame to get the sum of the value column, however, whilst doing this I would like to retain the min start and max end time of my df.
so my example output would be as follows :
id Start End Value
0 A 2019-01-01 00:00:00 2019-01-01 22:15:00 2007
1 B 2019-01-01 03:00:00 2019-01-02 00:15:00 1385
The only way I've sort of made this work is pass the min and max of each unique ID by start and end time, pass these to a list and then manually create the start and end times, but it was slow and messy and prone to error... hoping someone here can guide me as to what I'm missing.
Using groupby with agg
df.groupby('id').agg({'Start':'min','End':'max','Value':'sum'})#reset_index()
Out[92]:
Start End Value
id
A 2019-01-01 00:00:00 2019-01-01 22:15:00 2152
B 2019-01-01 03:00:00 2019-01-02 00:15:00 972

how to set a series into a slice of a dataframe?

I have temperature time series data in 15 minute intervals.
If temp value is missing, i want to take mean of temp values of last/next 10 days at same time and put it in place of nan.
This is my code
This returns a pandas series with the values i want to keep for na values.
pd.Series(df.index[(df.Temp.isna())]).apply(last10daysmean)
How do i put the above. into this one below?
df.Temp[df.Temp.isna()]
This returns the na slots.
I don't have the function last10daysmean from your question so I can substitute it with this:
def last10daysmean(x):
return "TenDaysMeanPlaceholder"
You should try to have sample data when you post a question but I can just make temp data now:
df = pd.DataFrame({
"Temp": [2, 3, 4, 5, 6, np.nan, 3, 4, np.nan]
})
This fills the isna rows with the output of our dummy version for your last10daysmean function:
df.Temp[df.Temp.isna()] = df.Temp[df.Temp.isna()].apply(last10daysmean)
You can try of writing the row value by value apply function
df = pd.DataFrame()
df['value'] = np.random.random(len(pd.date_range(start='2019-1-1',end='2019-1-2',freq='15Min')))*10
df.index = pd.date_range(start='2019-1-1',end='2019-1-2',freq='15Min')
df.loc[df['value']<2,'value'
] = np.nan
Sample Dataframe
value
2019-01-01 00:00:00 NaN
2019-01-01 00:15:00 6.100087
2019-01-01 00:30:00 7.953615
2019-01-01 00:45:00 7.214069
2019-01-01 01:00:00 3.697723
2019-01-01 01:15:00 5.772333
2019-01-01 01:30:00 NaN
2019-01-01 01:45:00 2.827144
Function to get slice of dataframe
def last10daysmean(x,ind):
df.loc[ind,'value'] = x.mean()
temp = df.index.map(lambda x: last10daysmean(df['value'].loc[x:x+10],x) if math.isnan(df.loc[x,'value']) else df.loc[x,'value'])
Out:
value
2019-01-01 00:00:00 5.901569
2019-01-01 00:15:00 6.100087
2019-01-01 00:30:00 7.953615
2019-01-01 00:45:00 7.214069
2019-01-01 01:00:00 3.697723
2019-01-01 01:15:00 5.772333
2019-01-01 01:30:00 5.594577
2019-01-01 01:45:00 2.827144
2019-01-01 02:00:00 6.409086

Splitting a datetime, python, pandas

Sorry I am new to asking questions on stackoverflow so I don't understand how to format properly.
So I'm given a Pandas dataframe that contains column of datetime which contains the date and the time and an associated column that contains some sort of value. The given dates and times are incremented by the hour. I would like to manipulate the dataframe to have them increment every 15 minutes, but retain the same value. How would I do that? Thanks!
I have tried :
df = df.asfreq('15Min',method='ffill').
But I get a error:
"TypeError: Cannot compare type 'Timestamp' with type 'long'"
current dataframe:
datetime value
00:00:00 1
01:00:00 2
new dataframe:
datetime value
00:00:00 1
00:15:00 1
00:30:00 1
00:45:00 1
01:00:00 2
01:15:00 2
01:30:00 2
01:45:00 2
Update:
The approved answer below works, but so does the initial code I tried above
df = df.asfreq('15Min',method='ffill'). I was messing around with other Dataframes and I seemed to be having trouble with some null values so I took care of that with a fillna statements and everything worked.
You can use TimedeltaIndex, but is necessary manually add last value for correct reindex:
df['datetime'] = pd.to_timedelta(df['datetime'])
df = df.set_index('datetime')
tr = pd.timedelta_range(df.index.min(),
df.index.max() + pd.Timedelta(45*60, unit='s'), freq='15Min')
df = df.reindex(tr, method='ffill')
print (df)
value
00:00:00 1
00:15:00 1
00:30:00 1
00:45:00 1
01:00:00 2
01:15:00 2
01:30:00 2
01:45:00 2
Another solution with resample and same problem - need append new value for correct appending last values:
df['datetime'] = pd.to_timedelta(df['datetime'])
df = df.set_index('datetime')
df.loc[df.index.max() + pd.Timedelta(1, unit='h')] = 1
df = df.resample('15Min').ffill().iloc[:-1]
print (df)
value
datetime
00:00:00 1
00:15:00 1
00:30:00 1
00:45:00 1
01:00:00 2
01:15:00 2
01:30:00 2
01:45:00 2
But if values are datetimes:
print (df)
datetime value
0 2018-01-01 00:00:00 1
1 2018-01-01 01:00:00 2
df['datetime'] = pd.to_datetime(df['datetime'])
df = df.set_index('datetime')
tr = pd.date_range(df.index.min(),
df.index.max() + pd.Timedelta(45*60, unit='s'), freq='15Min')
df = df.reindex(tr, method='ffill')
df['datetime'] = pd.to_datetime(df['datetime'])
df = df.set_index('datetime')
df.loc[df.index.max() + pd.Timedelta(1, unit='h')] = 1
df = df.resample('15Min').ffill().iloc[:-1]
print (df)
value
datetime
2018-01-01 00:00:00 1
2018-01-01 00:15:00 1
2018-01-01 00:30:00 1
2018-01-01 00:45:00 1
2018-01-01 01:00:00 2
2018-01-01 01:15:00 2
2018-01-01 01:30:00 2
2018-01-01 01:45:00 2
You can use pandas.daterange
pd.date_range('00:00:00', '01:00:00', freq='15T')

Get weekday/day-of-week for Datetime column of DataFrame

I have a DataFrame df like the following (excerpt, 'Timestamp' are the index):
Timestamp Value
2012-06-01 00:00:00 100
2012-06-01 00:15:00 150
2012-06-01 00:30:00 120
2012-06-01 01:00:00 220
2012-06-01 01:15:00 80
...and so on.
I need a new column df['weekday'] with the respective weekday/day-of-week of the timestamps.
How can I get this?
Use the new dt.dayofweek property:
In [2]:
df['weekday'] = df['Timestamp'].dt.dayofweek
df
Out[2]:
Timestamp Value weekday
0 2012-06-01 00:00:00 100 4
1 2012-06-01 00:15:00 150 4
2 2012-06-01 00:30:00 120 4
3 2012-06-01 01:00:00 220 4
4 2012-06-01 01:15:00 80 4
In the situation where the Timestamp is your index you need to reset the index and then call the dt.dayofweek property:
In [14]:
df = df.reset_index()
df['weekday'] = df['Timestamp'].dt.dayofweek
df
Out[14]:
Timestamp Value weekday
0 2012-06-01 00:00:00 100 4
1 2012-06-01 00:15:00 150 4
2 2012-06-01 00:30:00 120 4
3 2012-06-01 01:00:00 220 4
4 2012-06-01 01:15:00 80 4
Strangely if you try to create a series from the index in order to not reset the index you get NaN values as does using the result of reset_index to call the dt.dayofweek property without assigning the result of reset_index back to the original df:
In [16]:
df['weekday'] = pd.Series(df.index).dt.dayofweek
df
Out[16]:
Value weekday
Timestamp
2012-06-01 00:00:00 100 NaN
2012-06-01 00:15:00 150 NaN
2012-06-01 00:30:00 120 NaN
2012-06-01 01:00:00 220 NaN
2012-06-01 01:15:00 80 NaN
In [17]:
df['weekday'] = df.reset_index()['Timestamp'].dt.dayofweek
df
Out[17]:
Value weekday
Timestamp
2012-06-01 00:00:00 100 NaN
2012-06-01 00:15:00 150 NaN
2012-06-01 00:30:00 120 NaN
2012-06-01 01:00:00 220 NaN
2012-06-01 01:15:00 80 NaN
EDIT
As pointed out to me by user #joris you can just access the weekday attribute of the index so the following will work and is more compact:
df['Weekday'] = df.index.weekday
If the Timestamp column is a datetime value, then you can just use:
df['weekday'] = df['Timestamp'].apply(lambda x: x.weekday())
or
df['weekday'] = pd.to_datetime(df['Timestamp']).apply(lambda x: x.weekday())
You can get with this way:
import datetime
df['weekday'] = pd.Series(df.index).dt.day_name()
In case somebody else has the same issue with a multiindexed dataframe, here is what solved it for me, based on #joris solution:
df['Weekday'] = df.index.get_level_values(1).weekday
for me date was the get_level_values(1) instead of get_level_values(0), which would work for the outer index.
As of pandas 1.1.0 dt.dayofweek is deprecated, so instead of:
df['weekday'] = df['Timestamp'].dt.dayofweek
from #EdChum and #Artyom Krivolapov
you can now use:
df['weekday'] = df['Timestamp'].dt.isocalendar().day

Categories

Resources