如何将上限应用于 pandas DateTime
how to apply ceiling to pandas DateTime
假设我有一个 pandas 数据框,其中一列的值为 datetime64[ns]
。
Out[204]:
0 2015-03-20 00:00:28
1 2015-03-20 00:01:44
2 2015-03-20 00:02:55
3 2015-03-20 00:03:39
4 2015-03-20 00:04:32
5 2015-03-20 00:05:52
6 2015-03-20 00:06:36
7 2015-03-20 00:07:44
8 2015-03-20 00:08:56
9 2015-03-20 00:09:47
Name: DateTime, dtype: datetime64[ns]
有什么简单的方法可以将它们转换为时间后最近的分钟吗?即我想要以下内容:
Out[204]:
0 2015-03-20 00:01:00
1 2015-03-20 00:02:00
2 2015-03-20 00:03:00
3 2015-03-20 00:04:00
4 2015-03-20 00:05:00
5 2015-03-20 00:06:00
6 2015-03-20 00:07:00
7 2015-03-20 00:08:00
8 2015-03-20 00:09:00
9 2015-03-20 00:10:00
Name: DateTime, dtype: datetime64[ns]
我写了一段比较复杂的代码,首先将它们转换成字符串,然后提取00:09:47
的三部分,将它们转换成整数,然后除非最后一部分(秒)已经是00
,我将最后一部分(秒)设为 00
,将 1
添加到中间部分(分钟),除非中间部分(分钟)已经是 59
,在这种情况下它会添加到第一部分(小时)。然后将新的整数重新组合回一个字符串,然后重建回 DateTime
。
但我在想,可能已经有一个更简单的解决方案了。有人有什么建议吗?
* 编辑 *
@Jeff、@unutbu,感谢您的回答。在 SO 中我只能 select 一个答案,但两者都有效。
给定一个包含 dtype datetime64[ns]
列的 DataFrame,您可以
使用
df['date'] += np.array(-df['date'].dt.second % 60, dtype='<m8[s]')
添加适当的秒数获取上限。
例如,
import io
import sys
import numpy as np
import pandas as pd
StringIO = io.BytesIO if sys.version < '3' else io.StringIO
df = '''\
2015-03-20 00:00:00
2015-03-20 00:00:28
2015-03-20 00:01:44
2015-03-20 00:02:55
2015-03-20 00:03:39
2015-03-20 00:04:32
2015-03-20 00:05:52
2015-03-20 00:06:36
2015-03-20 00:07:44
2015-03-20 00:08:56
2015-03-20 00:09:47'''
df = pd.read_table(StringIO(df), sep='\s{2,}',
header=None, parse_dates=[0], names=['date'])
df['date'] += np.array(-df['date'].dt.second % 60, dtype='<m8[s]')
print(df)
产量
date
0 2015-03-20 00:00:00
1 2015-03-20 00:01:00
2 2015-03-20 00:02:00
3 2015-03-20 00:03:00
4 2015-03-20 00:04:00
5 2015-03-20 00:05:00
6 2015-03-20 00:06:00
7 2015-03-20 00:07:00
8 2015-03-20 00:08:00
9 2015-03-20 00:09:00
10 2015-03-20 00:10:00
我认为它可能需要一些工作,但我认为这大致就是您所追求的(我确定有一种方法可以使用 .snap
或偏移量 .rollforward
,但似乎无法使它们正常工作):
ps = pd.Series([
datetime(2015, 1, 1, 19, 18, 34), # roll up min, reset sec
datetime(2015, 1, 1, 1, 1, 1), # roll up min, reset sec
datetime(2015, 1, 1, 0, 0, 0), # do nothing
datetime(2015, 1, 1, 23, 59, 1), # roll day/hr/min, reset sec
datetime(2015, 1, 31, 23, 59, 1), # roll mth/day/hr/min, reset sec
datetime(2015, 12, 31, 23, 59, 1) # roll yr/month/day/hr/min - reset sec
])
ps[ps.dt.second != 0] = ps.apply(lambda L: (L + timedelta(minutes=1)).replace(second=0))
这给你:
0 2015-01-01 19:19:00
1 2015-01-01 01:02:00
2 2015-01-01 00:00:00
3 2015-01-02 00:00:00
4 2015-02-01 00:00:00
5 2016-01-01 00:00:00
这是另一种方式。减去差秒(有点像圆形)。这是矢量化的。
In [46]: df.date+pd.to_timedelta(-df.date.dt.second % 60,unit='s')
Out[46]:
0 2015-03-20 00:01:00
1 2015-03-20 00:02:00
2 2015-03-20 00:03:00
3 2015-03-20 00:04:00
4 2015-03-20 00:05:00
5 2015-03-20 00:06:00
6 2015-03-20 00:07:00
7 2015-03-20 00:08:00
8 2015-03-20 00:09:00
9 2015-03-20 00:10:00
dtype: datetime64[ns
这是另一种方式。将某物更改为另一个频率的周期使其四舍五入。 (请注意,这是一个有点笨拙的 ATM,因为周期作为一种列类型并不成熟)。这是矢量化的。
In [48]: pd.Series(pd.PeriodIndex(df.date.dt.to_period('T')+1).to_timestamp())
Out[48]:
0 2015-03-20 00:01:00
1 2015-03-20 00:02:00
2 2015-03-20 00:03:00
3 2015-03-20 00:04:00
4 2015-03-20 00:05:00
5 2015-03-20 00:06:00
6 2015-03-20 00:07:00
7 2015-03-20 00:08:00
8 2015-03-20 00:09:00
9 2015-03-20 00:10:00
dtype: datetime64[ns]
最后一种方法将始终舍入 'up',因为我们正在增加下限周期。
现在 pandas 中的内置方法 ceil()
可用于此目的。对于一系列日期时间,可以使用 Series.dt.ceil()
:
访问它
In[92]: t
Out[92]:
0 2015-03-20 00:00:28
1 2015-03-20 00:01:44
2 2015-03-20 00:02:55
3 2015-03-20 00:03:39
4 2015-03-20 00:04:32
5 2015-03-20 00:05:52
6 2015-03-20 00:06:36
7 2015-03-20 00:07:44
8 2015-03-20 00:08:56
9 2015-03-20 00:09:47
dtype: datetime64[ns]
In[93]: t.dt.ceil('min')
Out[93]:
0 2015-03-20 00:01:00
1 2015-03-20 00:02:00
2 2015-03-20 00:03:00
3 2015-03-20 00:04:00
4 2015-03-20 00:05:00
5 2015-03-20 00:06:00
6 2015-03-20 00:07:00
7 2015-03-20 00:08:00
8 2015-03-20 00:09:00
9 2015-03-20 00:10:00
dtype: datetime64[ns]
ceil()
接受频率参数。列出了它的字符串别名 here.
假设我有一个 pandas 数据框,其中一列的值为 datetime64[ns]
。
Out[204]:
0 2015-03-20 00:00:28
1 2015-03-20 00:01:44
2 2015-03-20 00:02:55
3 2015-03-20 00:03:39
4 2015-03-20 00:04:32
5 2015-03-20 00:05:52
6 2015-03-20 00:06:36
7 2015-03-20 00:07:44
8 2015-03-20 00:08:56
9 2015-03-20 00:09:47
Name: DateTime, dtype: datetime64[ns]
有什么简单的方法可以将它们转换为时间后最近的分钟吗?即我想要以下内容:
Out[204]:
0 2015-03-20 00:01:00
1 2015-03-20 00:02:00
2 2015-03-20 00:03:00
3 2015-03-20 00:04:00
4 2015-03-20 00:05:00
5 2015-03-20 00:06:00
6 2015-03-20 00:07:00
7 2015-03-20 00:08:00
8 2015-03-20 00:09:00
9 2015-03-20 00:10:00
Name: DateTime, dtype: datetime64[ns]
我写了一段比较复杂的代码,首先将它们转换成字符串,然后提取00:09:47
的三部分,将它们转换成整数,然后除非最后一部分(秒)已经是00
,我将最后一部分(秒)设为 00
,将 1
添加到中间部分(分钟),除非中间部分(分钟)已经是 59
,在这种情况下它会添加到第一部分(小时)。然后将新的整数重新组合回一个字符串,然后重建回 DateTime
。
但我在想,可能已经有一个更简单的解决方案了。有人有什么建议吗?
* 编辑 *
@Jeff、@unutbu,感谢您的回答。在 SO 中我只能 select 一个答案,但两者都有效。
给定一个包含 dtype datetime64[ns]
列的 DataFrame,您可以
使用
df['date'] += np.array(-df['date'].dt.second % 60, dtype='<m8[s]')
添加适当的秒数获取上限。
例如,
import io
import sys
import numpy as np
import pandas as pd
StringIO = io.BytesIO if sys.version < '3' else io.StringIO
df = '''\
2015-03-20 00:00:00
2015-03-20 00:00:28
2015-03-20 00:01:44
2015-03-20 00:02:55
2015-03-20 00:03:39
2015-03-20 00:04:32
2015-03-20 00:05:52
2015-03-20 00:06:36
2015-03-20 00:07:44
2015-03-20 00:08:56
2015-03-20 00:09:47'''
df = pd.read_table(StringIO(df), sep='\s{2,}',
header=None, parse_dates=[0], names=['date'])
df['date'] += np.array(-df['date'].dt.second % 60, dtype='<m8[s]')
print(df)
产量
date
0 2015-03-20 00:00:00
1 2015-03-20 00:01:00
2 2015-03-20 00:02:00
3 2015-03-20 00:03:00
4 2015-03-20 00:04:00
5 2015-03-20 00:05:00
6 2015-03-20 00:06:00
7 2015-03-20 00:07:00
8 2015-03-20 00:08:00
9 2015-03-20 00:09:00
10 2015-03-20 00:10:00
我认为它可能需要一些工作,但我认为这大致就是您所追求的(我确定有一种方法可以使用 .snap
或偏移量 .rollforward
,但似乎无法使它们正常工作):
ps = pd.Series([
datetime(2015, 1, 1, 19, 18, 34), # roll up min, reset sec
datetime(2015, 1, 1, 1, 1, 1), # roll up min, reset sec
datetime(2015, 1, 1, 0, 0, 0), # do nothing
datetime(2015, 1, 1, 23, 59, 1), # roll day/hr/min, reset sec
datetime(2015, 1, 31, 23, 59, 1), # roll mth/day/hr/min, reset sec
datetime(2015, 12, 31, 23, 59, 1) # roll yr/month/day/hr/min - reset sec
])
ps[ps.dt.second != 0] = ps.apply(lambda L: (L + timedelta(minutes=1)).replace(second=0))
这给你:
0 2015-01-01 19:19:00
1 2015-01-01 01:02:00
2 2015-01-01 00:00:00
3 2015-01-02 00:00:00
4 2015-02-01 00:00:00
5 2016-01-01 00:00:00
这是另一种方式。减去差秒(有点像圆形)。这是矢量化的。
In [46]: df.date+pd.to_timedelta(-df.date.dt.second % 60,unit='s')
Out[46]:
0 2015-03-20 00:01:00
1 2015-03-20 00:02:00
2 2015-03-20 00:03:00
3 2015-03-20 00:04:00
4 2015-03-20 00:05:00
5 2015-03-20 00:06:00
6 2015-03-20 00:07:00
7 2015-03-20 00:08:00
8 2015-03-20 00:09:00
9 2015-03-20 00:10:00
dtype: datetime64[ns
这是另一种方式。将某物更改为另一个频率的周期使其四舍五入。 (请注意,这是一个有点笨拙的 ATM,因为周期作为一种列类型并不成熟)。这是矢量化的。
In [48]: pd.Series(pd.PeriodIndex(df.date.dt.to_period('T')+1).to_timestamp())
Out[48]:
0 2015-03-20 00:01:00
1 2015-03-20 00:02:00
2 2015-03-20 00:03:00
3 2015-03-20 00:04:00
4 2015-03-20 00:05:00
5 2015-03-20 00:06:00
6 2015-03-20 00:07:00
7 2015-03-20 00:08:00
8 2015-03-20 00:09:00
9 2015-03-20 00:10:00
dtype: datetime64[ns]
最后一种方法将始终舍入 'up',因为我们正在增加下限周期。
现在 pandas 中的内置方法 ceil()
可用于此目的。对于一系列日期时间,可以使用 Series.dt.ceil()
:
In[92]: t
Out[92]:
0 2015-03-20 00:00:28
1 2015-03-20 00:01:44
2 2015-03-20 00:02:55
3 2015-03-20 00:03:39
4 2015-03-20 00:04:32
5 2015-03-20 00:05:52
6 2015-03-20 00:06:36
7 2015-03-20 00:07:44
8 2015-03-20 00:08:56
9 2015-03-20 00:09:47
dtype: datetime64[ns]
In[93]: t.dt.ceil('min')
Out[93]:
0 2015-03-20 00:01:00
1 2015-03-20 00:02:00
2 2015-03-20 00:03:00
3 2015-03-20 00:04:00
4 2015-03-20 00:05:00
5 2015-03-20 00:06:00
6 2015-03-20 00:07:00
7 2015-03-20 00:08:00
8 2015-03-20 00:09:00
9 2015-03-20 00:10:00
dtype: datetime64[ns]
ceil()
接受频率参数。列出了它的字符串别名 here.