Python:为什么使用 asfreq() 会删除我的最后一行数据?
Python: Why using asfreq() is dropping my last row of data?
我不明白为什么当我使用 pandas asfreq() 方法时它会删除我的最后一行数据。
data = rental_2012_09_to_2020_06.copy()
data.drop(columns='postcode', inplace=True)
data.columns = ['Quarter_End', 'Rental_Median']
data.index = pd.to_datetime(data['Quarter_End'])
data = data.asfreq(freq='Q', method='ffill')
data.drop(columns='Quarter_End', inplace=True)
data.info()
data.head()
结果运行 以上代码:
结果 运行 注释 asfreq()
行:
有什么想法吗?我阅读了文档,但没有提供有关此行为的详细信息。谢谢!
您按季度频率对 time-series 数据帧进行了重新采样:
data = data.asfreq(freq='Q', method='ffill')
根据pandas documentation,Q
频率别名代表“四分之一结束频率”。原始数据框中的第一个条目是 2012 年 9 月 1 日,最后一个条目是 2020 年 6 月 1 日。
我猜测 asfreq
会重新采样时间范围,以便重新采样的范围落在原始范围内(但据我所知,这没有记录)。由于 2012 年 1 月 9 日之后的第一季度末是 2012 年 9 月 30 日,而 2020 年 1 月 6 日之前的最后一个季度末是 2020 年 3 月 31 日,因此得出的日期范围是 2012 年 9 月 30 日到 2003 年 3 月 31 日/2020(包括两者),每季度一次,产生 31 个样本。这恰好比原始数据帧少一个样本这一事实仅仅是巧合(从某种意义上说,它取决于原始日期时间范围)。
编辑: 通过深入研究 pandas 源代码,我发现了这种行为的确切位置 defined/documented。在 asfreq
的调用图中,有一个生成器 generate_range
(具体来说,pandas.core.arrays.datetimes.generate_range
,从 v1.1.2 开始),它是在一个范围内定义日期时间值的核心功能[start, end]
值之间有一定的时间偏移(即频率)。
def generate_range(start=None, end=None, periods=None, offset=BDay()): ...
其文档字符串指定:
Notes
-----
* [...]
* If both start and end are specified, the returned dates will
satisfy start <= date <= end.
回复评论
我认为您可能误解了 asfreq
的作用。它不是简单地将值移动到落在频率边界上的最近时间;相反,它创建了一个全新的系列,使用来自原始数据的数据,就好像它是在特定时间戳(由频率指定)采样的一样。也就是说,您是 resampling 您的数据。
尝试从调用中删除 method='ffil'
参数:您会看到新系列将全部为 NaN
s(除了时间戳与原始时间戳重合的情况series)—这是因为它不知道样本的值在未知时间戳应该是多少。
import numpy as np
import pandas as pd
import datetime as dtm
index = pd.DatetimeIndex(data=[
dtm.datetime(2019, 12, 1),
dtm.datetime(2020, 2, 17),
dtm.datetime(2020, 3, 31),
dtm.datetime(2020, 6, 1),
])
series = pd.Series([1, 2, 3, 4], index=index)
>>> series
2019-12-01 1
2020-02-17 2
2020-03-31 3
2020-06-01 4
dtype: int64
>>> series.asfreq('Q')
2019-12-31 NaN
2020-03-31 3.0
Freq: Q-DEC, dtype: float64
如果您考虑一下,这就非常有意义:如果原始数据中没有记录该日期的值,pandas
应该如何知道 2019-12-31
上的值?
当然,拥有一个充满 NaN
的系列并不是很有用,因此我们需要找到一种方法来 推断 从可用数据中丢失的数据.这就是 method='ffil'
(复制最后一个可用值)或 method='bfil'
(复制下一个可用值)等填充方法发挥作用的地方。
现在,回到您的问题:没有 2020-06-30 记录的唯一原因是上面 generate_range
的文档字符串指定的内容:
[...] the returned dates will satisfy start <= date <= end
即,转换后的日期时间范围将始终落在 原始范围内,而 2020-06-30 落在 范围之外(因为原始范围的 end
是 2020-06-01).
同样,考虑到我们刚才讨论的内容,这也是有道理的:通常您想要推断重采样序列中的缺失值(使用原始序列中的值),并且它总是更容易(也更安全) interpolate(即猜测值在两个其他值之间的时间步长内是什么),而不是 extrapolate(即猜测原始范围之外的值,无论是在开始之前还是在结束之后),而前者只有在将新的、重新采样的范围保持在原始范围内的情况下才能保证——更不用说 'ffil'
和 'bfil'
都需要这个条件为了正常工作(你不能 forward-fill 如果它出现在 start
之前的第一个值,你不能 backwards-fill 如果它出现在 end
之后的最后一个值)。
如果这不是您想要的行为,而您只是想简单地移动时间步长,则您必须完全执行其他操作。可以给索引加一个偏移量,例如:
index = pd.date_range(
dtm.datetime(2019, 9, 1),
dtm.datetime(2020, 6, 1),
freq=pd.DateOffset(months=3)
)
series = pd.Series([1, 2, 3, 4], index=index)
>>> index
DatetimeIndex(['2019-09-01', '2019-12-01', '2020-03-01', '2020-06-01'], dtype='datetime64[ns]', freq='<DateOffset: months=3>')
>>> index + pd.tseries.offsets.MonthEnd()
DatetimeIndex(['2019-09-30', '2019-12-31', '2020-03-31', '2020-06-30'], dtype='datetime64[ns]', freq='<DateOffset: months=3>')
我不明白为什么当我使用 pandas asfreq() 方法时它会删除我的最后一行数据。
data = rental_2012_09_to_2020_06.copy()
data.drop(columns='postcode', inplace=True)
data.columns = ['Quarter_End', 'Rental_Median']
data.index = pd.to_datetime(data['Quarter_End'])
data = data.asfreq(freq='Q', method='ffill')
data.drop(columns='Quarter_End', inplace=True)
data.info()
data.head()
结果运行 以上代码:
结果 运行 注释 asfreq()
行:
有什么想法吗?我阅读了文档,但没有提供有关此行为的详细信息。谢谢!
您按季度频率对 time-series 数据帧进行了重新采样:
data = data.asfreq(freq='Q', method='ffill')
根据pandas documentation,Q
频率别名代表“四分之一结束频率”。原始数据框中的第一个条目是 2012 年 9 月 1 日,最后一个条目是 2020 年 6 月 1 日。
我猜测 asfreq
会重新采样时间范围,以便重新采样的范围落在原始范围内(但据我所知,这没有记录)。由于 2012 年 1 月 9 日之后的第一季度末是 2012 年 9 月 30 日,而 2020 年 1 月 6 日之前的最后一个季度末是 2020 年 3 月 31 日,因此得出的日期范围是 2012 年 9 月 30 日到 2003 年 3 月 31 日/2020(包括两者),每季度一次,产生 31 个样本。这恰好比原始数据帧少一个样本这一事实仅仅是巧合(从某种意义上说,它取决于原始日期时间范围)。
编辑: 通过深入研究 pandas 源代码,我发现了这种行为的确切位置 defined/documented。在 asfreq
的调用图中,有一个生成器 generate_range
(具体来说,pandas.core.arrays.datetimes.generate_range
,从 v1.1.2 开始),它是在一个范围内定义日期时间值的核心功能[start, end]
值之间有一定的时间偏移(即频率)。
def generate_range(start=None, end=None, periods=None, offset=BDay()): ...
其文档字符串指定:
Notes ----- * [...] * If both start and end are specified, the returned dates will satisfy start <= date <= end.
回复评论
我认为您可能误解了 asfreq
的作用。它不是简单地将值移动到落在频率边界上的最近时间;相反,它创建了一个全新的系列,使用来自原始数据的数据,就好像它是在特定时间戳(由频率指定)采样的一样。也就是说,您是 resampling 您的数据。
尝试从调用中删除 method='ffil'
参数:您会看到新系列将全部为 NaN
s(除了时间戳与原始时间戳重合的情况series)—这是因为它不知道样本的值在未知时间戳应该是多少。
import numpy as np
import pandas as pd
import datetime as dtm
index = pd.DatetimeIndex(data=[
dtm.datetime(2019, 12, 1),
dtm.datetime(2020, 2, 17),
dtm.datetime(2020, 3, 31),
dtm.datetime(2020, 6, 1),
])
series = pd.Series([1, 2, 3, 4], index=index)
>>> series
2019-12-01 1
2020-02-17 2
2020-03-31 3
2020-06-01 4
dtype: int64
>>> series.asfreq('Q')
2019-12-31 NaN
2020-03-31 3.0
Freq: Q-DEC, dtype: float64
如果您考虑一下,这就非常有意义:如果原始数据中没有记录该日期的值,pandas
应该如何知道 2019-12-31
上的值?
当然,拥有一个充满 NaN
的系列并不是很有用,因此我们需要找到一种方法来 推断 从可用数据中丢失的数据.这就是 method='ffil'
(复制最后一个可用值)或 method='bfil'
(复制下一个可用值)等填充方法发挥作用的地方。
现在,回到您的问题:没有 2020-06-30 记录的唯一原因是上面 generate_range
的文档字符串指定的内容:
[...] the returned dates will satisfy
start <= date <= end
即,转换后的日期时间范围将始终落在 原始范围内,而 2020-06-30 落在 范围之外(因为原始范围的 end
是 2020-06-01).
同样,考虑到我们刚才讨论的内容,这也是有道理的:通常您想要推断重采样序列中的缺失值(使用原始序列中的值),并且它总是更容易(也更安全) interpolate(即猜测值在两个其他值之间的时间步长内是什么),而不是 extrapolate(即猜测原始范围之外的值,无论是在开始之前还是在结束之后),而前者只有在将新的、重新采样的范围保持在原始范围内的情况下才能保证——更不用说 'ffil'
和 'bfil'
都需要这个条件为了正常工作(你不能 forward-fill 如果它出现在 start
之前的第一个值,你不能 backwards-fill 如果它出现在 end
之后的最后一个值)。
如果这不是您想要的行为,而您只是想简单地移动时间步长,则您必须完全执行其他操作。可以给索引加一个偏移量,例如:
index = pd.date_range(
dtm.datetime(2019, 9, 1),
dtm.datetime(2020, 6, 1),
freq=pd.DateOffset(months=3)
)
series = pd.Series([1, 2, 3, 4], index=index)
>>> index
DatetimeIndex(['2019-09-01', '2019-12-01', '2020-03-01', '2020-06-01'], dtype='datetime64[ns]', freq='<DateOffset: months=3>')
>>> index + pd.tseries.offsets.MonthEnd()
DatetimeIndex(['2019-09-30', '2019-12-31', '2020-03-31', '2020-06-30'], dtype='datetime64[ns]', freq='<DateOffset: months=3>')